0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Node.js で WASM を使ったファイルの入出力

0
Last updated at Posted at 2025-11-28

この記事 の環境を使い 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-libraryEXPORTED_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
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?