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

# はじめに
とあるプログラムがC言語で書かれていて、それをwebシステムとしてnodeで実行したいと思ったのでやり方を調べました。整理すると、大したことはないです。

## 概要
準備
1. c言語のソースをコンパイルして、モジュールを作成
1. Webサーバーとして`express`を起動する

アクセス時
1. `express`のサーバー処理がモジュールをキックし、標準出力を得る
1. 標準出力を元にクライアントへ返す


## 作ったソース
https://github.com/yo16/gcc_render_prac

結果を一応貼っておくと、↓こういう文字を返すだけのwebシステムですｗ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/722610/4dabc6dd-d932-be8d-4869-039f394f6d05.png)


# 詳細
## 全体
フォルダ構成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/722610/c426624d-90b3-de9e-c866-166c1c1a18f7.png)
役割は、`scripts`がc言語関係で、ソースとビルド用のスクリプト。`server`が`express`のソースです。
ちなみに最終的に、c言語のモジュールを`server`の下へ置きます。

## c言語のソース
```c: hello.c
#include <stdio.h>

int main() {
    printf("Hello, World!(c言語より！)\n");
    return 0;
}
```
なんでもいいんです。標準出力すれば。
日本語のやり取りができることの確認もしてます。

## c言語ソースのコンパイル
Render上でビルドしてほしいので、`package.json`で次のように設定しておきます。
```json: package.json（の一部）
  "scripts": {
    "start": "node ./server/app.js",
    "build": "npm install && node ./scripts/build-with-c.js"
  },
```
Renderの設定で、ビルドは`npm run build`、起動は`npm run start`とすることを想定しています。

```JavaScript: 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 = `./server/${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をコピー');
});
```
ファイル名をごちゃごちゃやっているのは、開発環境ではexeにしたかったというだけです。
本筋は、`exec`で`gcc`を使ってコンパイルし、できたモジュールを`copyFileSync`で`./server/`へコピーしてます。
`gcc`は、Renderが持ってます。

https://docs.render.com/native-runtimes#tools-and-utilities


## expressサーバー
```JavaScript:app.js
const express = require("express")
const path = require("path");
const { exec } = require("child_process");


const app = express()
const port = 3000

app.get('/', (req, res) => {
    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}`)
})
```
`exec`で、出力したファイルを非同期で実行し、エラーなく進んだら、その標準出力`stdout`を`/`の戻り値にしてます。

あ、以上でした。この結果が、最初にも貼った下記のもの。日本語も渡ってます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/722610/4dabc6dd-d932-be8d-4869-039f394f6d05.png)

# おわりに
JavaScriptでひとまずできました。Renderはほんと便利で感謝です。

つぎはTypeScriptでやってみます。
実は最初TypeScriptでやったらいろいろな問題が出たのですが、問題の切り分けが大変でした。（そのときのアプリ名がapp-c-lang。）そのため先にJavaScriptでやって足掛かりを作ってみた次第です。JavaScriptでできた上でのTypeScriptなら、TypeScript特有の手続きは何なのかっていうことが分かりやすいかなと思って。