この記事 の環境を使い JavaScript と C++ で OS 上のファイルを操作する。
プログラム自体は検索したものを流用しているので特筆すべきことはないが CMakeLists.txt については多少注意が必要となる。
ファイル一覧
以下は構成するファイルの一覧となる。
C:\wasm\project\17-nodefs\.vscode\launch.json
{
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Node Debug",
"skipFiles": [
"<node_internals>/**"
],
"program": "${workspaceFolder}\\main.mjs"
}
]
}
C:\wasm\project\17-nodefs\CMakePresets.json
{
"version": 5,
"cmakeMinimumRequired": {
"major": 3,
"minor": 20,
"patch": 0
},
"configurePresets": [
{
"name": "wasm-config",
"hidden": true,
"generator": "Ninja Multi-Config",
"binaryDir": "${sourceDir}/build",
"cacheVariables": {
"CMAKE_CXX_STANDARD": "17",
"CMAKE_CXX_STANDARD_REQUIRED": "ON",
"CMAKE_CXX_EXTENSIONS": "OFF",
"CMAKE_TOOLCHAIN_FILE": "$env{EMSDK}/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake",
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON",
"CMAKE_CONFIGURATION_TYPES": "Debug;Release"
}
},
{
"name": "wasm-config-debug",
"inherits": "wasm-config",
"cacheVariables": {
}
},
{
"name": "wasm-config-release",
"inherits": "wasm-config",
"cacheVariables": {
}
}
],
"buildPresets": [
{
"name": "wasm-build-debug",
"configurePreset": "wasm-config-debug",
"verbose": true,
"configuration": "Debug"
},
{
"name": "wasm-build-release",
"configurePreset": "wasm-config-release",
"verbose": true,
"configuration": "Release"
}
]
}
C:\wasm\project\17-nodefs\CMakeLists.txt
- set(EMSDK) を使い Emscripten の環境変数を内部変数に設定
-
--js-library と EXPORTED_RUNTIME_METHODS の記述によりグルーコードの
NODEFSが利用できるようなる
cmake_minimum_required(VERSION 3.20)
project(17_nodefs LANGUAGES CXX)
# 環境変数の EMSDK(C:\wasm\tool\emsdk-4.0.16) を変数(EMSDK) に設定
set(EMSDK $ENV{EMSDK})
message(STATUS "EMSDK is [${EMSDK}]")
add_executable(17_nodefs main.cpp)
target_link_options(17_nodefs PRIVATE
$<$<CONFIG:Debug>:-gsource-map -sASSERTIONS=1 -sSAFE_HEAP=1>
"-sEXPORT_ES6=1"
"-sEXPORTED_RUNTIME_METHODS=FS,NODEFS"
"--js-library=${EMSDK}/upstream/emscripten/src/lib/libnodefs.js"
)
C:\wasm\project\17-nodefs\main.mjs
OS 上のディレクトリを仮想ファイルシステムにマウントし、ファイルの内容をコンソールに出力した後で日時をファイルに出力する。
import path from 'path';
import fs from 'fs';
import Module from './build/Debug/17_nodefs.js';
const dataFileName = 'data.txt'
// OS からみたパス
const osTempDir = path.join(process.cwd(), 'os_tmp')
const osFullPath = path.join(`${osTempDir}`, dataFileName)
// 仮想ファイルシステムからみたパス
const esTempDir = '/es_tmp'
const esFullPath = `${esTempDir}/${dataFileName}`
console.log(`esFullPath=[${esFullPath}], osFullPath=[${osFullPath}]`)
if (!fs.existsSync(osTempDir))
{
// OS 上のディレクトリを作成
fs.mkdirSync(osTempDir);
}
const preRun = [emsModule => {
const { FS, NODEFS } = emsModule
// 仮想ファイルシステムにディレクトリを作り、OS のディレクトリをマウント
FS.mkdir(esTempDir)
FS.mount(NODEFS, { root: osTempDir }, esTempDir)
const info = FS.analyzePath(esFullPath)
if (info.exists)
{
// 現在のファイルの内容を表示
const inStr = FS.readFile(esFullPath, {encoding: 'utf8'})
console.log(inStr.trim())
}
// 現時刻を追加で書き込み
const outStr = `|JS: ${(new Date()).toLocaleString()}\n`
const outBin = new TextEncoder().encode(outStr)
const stream = FS.open(esFullPath, 'a')
FS.write(stream, outBin, 0, outBin.length)
FS.close(stream);
}]
const emsModule = await Module({ arguments: [esFullPath], preRun })
console.log('DONE')
C:\wasm\project\17-nodefs\main.cpp
ディレクトリのマウントを除き、処理内容は JavaScript と同じ。
// UTF-8N CRLF
#include <emscripten.h>
#include <iostream>
#include <chrono>
#include <fstream>
#include <cstdlib>
static void print_args(int argc, char** argv);
int main(int argc, char** argv, char** envp)
{
print_args(argc, argv);
std::string esFullPath{ argv[1] };
// 現在のファイルの内容を表示
if (std::ifstream ifs{esFullPath})
{
std::cout << ifs.rdbuf();
ifs.close();
}
// 現時刻を追加で書き込み
if (std::ofstream ofs{esFullPath, std::ios::app})
{
auto now = std::chrono::system_clock::now();
std::time_t t = std::chrono::system_clock::to_time_t(now);
std::tm tm = *std::localtime(&t);
char buf[64];
std::strftime(buf, sizeof(buf), "%Y/%m/%d %H:%M:%S", &tm);
ofs << "|C++: " << buf << std::endl;
ofs.close();
}
return EXIT_SUCCESS;
}
void print_args(int argc, char** argv)
{
std::cout << "C++ main(argc=" << argc << ", argv=[";
for (int i=0; i<argc; ++i)
{
if (i) {
std::cout << ",";
}
std::cout << "'" << argv[i] << "'";
}
std::cout << "])" << std::endl;
}
// EOF
実行結果
上記のプログラムをデバッガで実行するとコンソールに以下のような出力が行われる。
esFullPath=[/es_tmp/data.txt], osFullPath=[C:\wasm\project\17-nodefs\os_tmp\data.txt]
|JS: 2025/11/28 21:46:20
|C++: 2025/11/28 21:46:20
C++ main(argc=2, argv=['C:/wasm/project/17-nodefs/main.mjs','/es_tmp/data.txt'])
|JS: 2025/11/28 21:46:20
|C++: 2025/11/28 21:46:20
|JS: 2025/11/28 21:46:23
DONE
JavaScript と C++ で同じファイルに出力できていることを確認できる。
C:\wasm\project\17-nodefs>type C:\wasm\project\17-nodefs\os_tmp\data.txt
|JS: 2025/11/28 21:46:20
|C++: 2025/11/28 21:46:20
|JS: 2025/11/28 21:46:23
|C++: 2025/11/28 21:46:23