0
0

Node.jsのexpress(TypeScript版)でc言語のプログラムをRenderで実行

Last updated at Posted at 2024-06-19

はじめに

さきほど、JavaScript版のexpressでc言語のプログラムを実行するのを書いたばかりですがその続編で、TypeScript版をお届けします。

この記事は、c言語とかRenderとかはあまり書かず、JavaScriptでexpressを作ったのと同じことをTypeScriptでやるにはどうするかっていう、基礎に立ち戻った感じになりそうです。

c言語のコンパイルは、JavaScriptのままとします。(そこをTypeScriptにこだわる必要性を感じなかったので)

githubのリポジトリは、JavaScriptと同じで、フォルダを「app-c-lang2-ts」にしました。こちらもRenderで動作確認済み。

手順

1. 初期処理

mkdir app-c-lang2-ts
cd app-c-lang2-ts
npm init -y
npx tsc --init
npm install express
npm install --save-dev @types/express

ここから!?っていう感じですが、基礎なので。

2. サーバーをコーディング

expressサーバーです。

/server/app.ts
import express, { Request, Response } from "express";
import path from "path";
import { exec } from "child_process";

const app = express()
const port = 3000

app.get('/', (req: Request, res: Response) => {
    const outputFileName = 'hello' + (process.platform === 'win32' ? '.exe' : '');
    const buildOutputPath = path.join(__dirname, outputFileName);

    exec(buildOutputPath, (runError, stdout, stderr) => {
        if (runError) {
            console.error(`実行エラー: ${runError}`);
            return res.status(500).send(`実行エラー: ${runError}`);
        }
        if (stderr) {
            console.error(`標準エラー出力: ${stderr}`);
            return res.status(500).send(`標準エラー出力: ${stderr}`);
        }
        res.send(stdout);
    });
})

app.listen(port, () => {
    console.log(`Example app listening on port ${port}`)
})

JavaScriptとの違いは、app.get('/', (req: Request, res: Response) => {と型定義しただけです。そのためのimportと。
あ、ESMになっているので、requireじゃなくてimportに変えてます。

3. package.jsonでビルドと実行の仕方を指示

npm run buildで行うことは3つ

  1. 依存するモジュールをインストール
  2. TypeScriptをJavaScriptへ変換して、distディレクトリへ出力
  3. c言語ソースをコンパイルして、distディレクトリへ出力

npm run startでは、distディレクトリに出力されたapp.js(JavaScript)を実行する。

package.json(一部)
  "scripts": {
    "start": "node ./dist/app.js",
    "build": "npm install && npx tsc && node ./scripts/build-with-c.js"
  },

TypeScriptを直接実行するわけでなく、distに出力されたJavaScriptを実行するんだということがわかっていれば、難しい話ではないですが、たぶん初心者は躓くと思います。そのため、c言語のモジュールもdistディレクトリへコピーします。(次)

4. TypeScriptの出力先を設定

tsconfig.json
~~省略~~
  "outDir": "./dist",
~~省略~~

outDirという項目に、./distを書いておきます。
そうすると、distフォルダに出力されます。

5. c言語ソースのコンパイルスクリプト

ここは、JavaScript版と比べて処理は変更していないので、その話は割愛します。JavaScript版をご覧ください。
コンパイル後のコピー先であるbuildOutputFilePathdistにしている点だけが変更点です。

build-with-c.js
const { exec } = require('child_process');
const { copyFileSync } = require('fs');
const { platform } = require('os');

// コンパイルするCプログラムのパスと出力ファイル名
const scriptsDirName = "scripts";
const inputFilePath = `./${scriptsDirName}/hello.c`;
const outputFileName = 'hello' + (platform() === 'win32' ? '.exe' : '');
const outputFilePath = `./${scriptsDirName}/${outputFileName}`;
const buildOutputFilePath = `./dist/${outputFileName}`;

// Cプログラムをコンパイル
exec(`gcc ${inputFilePath} -o ${outputFilePath}`, (compileError) => {
    if (compileError) {
        console.error(`コンパイルエラー: ${compileError}`);
        process.exit(1);
    }
    console.log('Cプログラムのコンパイル成功');

    // コンパイルしたプログラムをビルドディレクトリにコピー
    copyFileSync(outputFilePath, buildOutputFilePath);
    console.log('ビルドディレクトリにhelloをコピー');
});

ここもTypeScriptにする必要性ってあるのかな。c言語のソースが複雑になっても、ここが複雑になるわけではないし、型をしっかり確認したいこともあんまりないような。
そしてTypeScriptにすることで、package.jsonなどの設定系の変更が必要になり、そっちが複雑になることを避けました。

以上です。

おわりに

前回のJavaScript版の"おわりに"に書いた通り、やはりJavaScriptで実装した後にTypeScriptで書き直せば、そんなにハマることはありませんでした。

あえていえば私は、最初、c言語のコンパイルもTypeScriptでやろうとして、「あーもう!」ってなってました。なので今回はいろいろ言い訳をして、そこは見ないことにしたらうまくいきました。🙈

次のステップがあるとしたら、ここまではできたから、次は「c言語のコンパイルをTypeScript」ということだけに着目すればよくなって、問題もすっきりして、案外簡単に解決できるかもしれないです。でも私はいまのところ、メリットを感じないのでやりませんが。

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