めっちゃシンプルな更新監視型ビルドツールが欲しい

Hotot3 の core/scriptsCoffeeScript で書かれているので、LiveScript で書き直したりするようなことをしていた。

じゃっばすくりぷよにコンパイルする時に lsc--watch 付けてやってたんだけど、コンパイルに失敗するとわけ不明なエラーを吐いて落ちる。
これが一番ムッカーって来たような感じ。

それで他のツールを探してみたんだけど、 Ruby の Guard とか Watchr とかなんかエラー起こるし、よくわからないので、 全然更新を監視しないんだけど、LiveScript ファイルを取ってきて全部コンパイルする Ruby すくりぷよ書いた。

# -*- mode: ruby; -*-

def watch(pattern)
  found_files = Dir.glob pattern

  yield found_files
end

def echo_and_system(command)
  puts command
  system command
end

watch 'core/scripts/*.ls' do |matching|
  matching.each do |filename|
    echo_and_system "lsc --no-header --bare -c #{filename}"
  end
end

単に watch メソッドを呼びだすと更新されたほげファイルを見つけてブロックを実行してくれるやつが欲しい。

Socket.IO やってみた

const Http      = require( 'http' );
const Express   = require( 'express' );
const Socket_IO = require( 'socket.io' );

const app = Express();

const server = Http.createServer( app );

const io = Socket_IO( server );

const port = 3000;

app.get( '/', (request, response) => {
    response.sendFile( __dirname + '/client/index.html' );
} );

server.listen( port, () => {
    console.log( 'server listening at port %d', port );
} );

io.on( 'connection', socket => {
    console.log( 'a user connected' );

    socket.on( 'chat message', message => {
        console.log( `message: ${message}` );
    } )

    socket.on( 'disconnect', () => {
        console.log( 'user disconnected' );
    } );
} );

a user connected が表示されない………。
\(^o^)/オワタ

const server = require( 'http' ).createServer();

と:

const Http = require( 'http' )

const server = Http.createServer();

は一緒のはずだよね???

ファイルの順番の話

こーいう F# のソースファイルがありますよね。

Batteries.Env モジュールと:

// env.fs
module Batteries

open System

module Env =
  let at (index : int) =
    let argv = Environment.GetCommandLineArgs()
    argv.[index]

  let get = at 

それを参照する main.fs

open Batteries

printfn "%A" <| Env.get 0

入力するファイルの順番に違いがあったってご存知でした?
これだとビルドできるのに:

% fsharpc ./env.fs ./main.fs -o ./battery-env.exe

こっちだとビルドできない:

% fsharpc ./main.fs ./env.fs -o ./battery-env.exe

╰( ´◔ ω ◔ `)╯ 普通は Visual Studio に任せるから気が付かないよな

Argu すごい

Argu は F# 製のコマンドラインパーサーだよ。

gmake のオプションを前半だけ書いてみた。

type Arguments =
  | [<AltCommandLine("-B")>] Always_Make
  | [<AltCommandLine("-C")>] Directory             of dir        : string
  |                          Debug                 of mode       : DebugMode option
  | [<AltCommandLine("-e")>] Environment_Overrides
  |                          Eval                  of expr       : string
  | [<AltCommandLine("-f")>] File                  of file       : string
  | [<AltCommandLine("-i")>] Ignore_Errors
  | [<AltCommandLine("-I")>] Include_Dir           of dir        : string
  | [<AltCommandLine("-h")>] Jobs                  of n          : int option
  | [<AltCommandLine("-k")>] Keep_Going
with
  interface IArgParserTemplate with
    member this.Usage =
      match this with
        | Always_Make             -> "Unconditionally make all targets."
        | Directory _             -> "Change to <dir> before doing anything."
        | Debug _                 -> "Print various types of debugging information."
        | Environment_Overrides   -> "Environment variables override makefiles."
        | Eval _                  -> "Evaluate <expr> as a makefile statement."
        | File _                  -> "Read <file> as a makefile."
        | Ignore_Errors           -> "Ignore errors from recipes."
        | Include_Dir _           -> "Search <dir> for included makefiles."
        | Jobs _                  -> "Allow <n> jobs at once; infinite jobs with no arg."
        | Keep_Going              -> "Keep going when some targets can't be made."

DebugMode に何を入れるのかわからなかったので、スキップ。
書くならこんな感じに:

type DebugMode =
  | A = 1
  | B = 2
  | C = 3

コマンドラインをパースする

コマンドラインをパースするにはパーサーを作成する:

open Argu

let parser = ArgumentParser.Create<Arguments>( programName = "make.exe", errorHandler = ProcessExiter() )

第 2 引数の errorHandler = ProcessExiter() を忘れると恐ろしいことが起こるので注意。
後は、argv を引数に parserParse() メソッドか ParseCommandLine() メソッドを呼び出すだけ:

let results = parser.ParseCommandLine argv

すると、Arguments [] が返ってくる。
ので、これをあれこれする。

らしい。

printfn とかで出力する時に

最近までこんな風にやってたんだけど:

let name = user.GetName()    // printfn "Hello, %s!" user.GetName() はエラーになっちゃうので
                             // 一旦、変数に束縛する。

printfn "Hello, %s!" name

別にそんなことをする必要はなかったらしい。 <| を使うんだルーク!!

printfn "Hello, %s!" <| user.GetName()

ヽ(=´▽`=)ノ

よく考えると coke とか joke とかいい名前があるじゃまいか

よさげなビルドツールを作ることはよさげな開発人生に一条のいい感じの光を投げかける的ななんかアレなわけです。
以下の C 言語なソースコードをビルドすることを考えてみましょう:

// main.c
#include <stdio.h>

int main() {
    printf( "Hello, World!\n" );

    return 0;
}

このようなケースは実の所あんまりないんですが、えーっと考えるのが簡単だよねって思ったのでそーいうことにしています。

SCons 風のビルドツール

SCons さんは Python 製のビルドツールっていうかなんかそれです。
ごっごる神がお作りになった次世代ビルドツールである Bazel だって SCons さん風に*1ビルドスクリプトを書きます。

const joke = require( '../lib/' );

joke.program( 'main.c' );

こうして見ると、JavaScript にはもってこいのような気がしなくもないと云うか何と云うか………。

CMake 風のビルドツール

CMake さんは Makefile を生成するメタビルドツールだと云うこともできるかもしれません。
めんどくさい AutoTools よりは佳いと思われます。

const joke = require( 'joke' );

joke.project( 'hello-example' );

joke.addExecutable( 'main', 'main.c' );

っていうか、パッと見、SCons とあんまり変わってないですね………。

それで?

joke を SCons 風なビルドツールとして作ってみようかなと思ったり思わなかったり……………。

*1:ドキュメント見ただけだから、そんなことはないかもしれない

RequestPolicy じゃなくて RequestPolicy Continued にしたら捗った

いままで、RequestPolicy を愛用していたんだけど、全然更新されていないのに気がついたので、RequestPolicy Continued に切り替えた。

(๑´ڡ`๑) めっちゃ使いやすい

class が export キーワードで export できなくてちょっと悲しかった

node のバージョンはこんな感じ:

% node --version
v6.3.1

wasp を書いてたら、

// ↓ class は書けるのに、export ができない!!!><
/* export */ class Emitter {
    constructor() {
        this.events = [];
    }

    on(name, cb) {
        this.events = ( this.events || [] );
        this.events[name] = cb;

        return this;
    }

    emit(name, that) {
        let self = this;

        let events = self.events[name] || [];

        events.forEach( anEvent => {
            anEvent.call( self, that );
        } );
        return self;
    }
}

// ↓ ので、従来通りの module.exports に Emitter を設定する。
module.exports = Emitter;

という感じだったので、悲しい。

ビルドツール Wasp とかどうかな

Fly を見ていたらこういう風に書けたって良いんじゃないかなと思った:

const gcc = require( 'wasp-gcc' );

const paths = {
    scripts: [ 'src/**/*.c' ],
    dist: 'obj'
};

export default function () {
    this.ready( 'build' );
}

export function compile() {
    this.from( paths.scripts )
        .gcc()
        .to( paths.dist );
}

うん、source() とか target() じゃなくて、from() とか to() って書けた方が良いと思う。

モジュールが Fly オブジェクトのメソッドを使えるようにするのってどうするんだーーーー?

Nemerle やってみた

インストール

yaourt でインストールできる:

% yaourt -S nemerle

へろーわーるど

using System;

Console.WriteLine( "Hello, World!" );

このファイルを hello.n とすると、ncc を使ってこんな風にコンパイルできる:

% ncc ./hello.n -o hello.exe

関数

関数は def から始まる:

def add(a : int, b : int) : int {
    a + b;
}