tech

[English] [Japanese]

23. Build

This time, I will explain how to build a project that uses LuneScript.

+LuneScript provides a transcompiler to use from the command line,++We do not provide build tools specifically for LuneScript.++Therefore, the general build method is explained here.

There are the following methods to transcompile LuneScript.

  • individual build
  • batch build

Batch build transcompiles multiple .lns files at once.

individual build

To transcompile to Lua, run the following command:

$ lnsc hello.lns save

This will transcompile hello.lns and output the converted code to hello.lua.

This hello.lua can be executed with the Lua command.

$ lua hello.lua

By doing this for all .lns files, you can transcompile all .lns files.

However, if you have many .lns files, this is inefficient.

batch build

Batch build transcompiles multiple .lns files at once.

To specify multiple .lns files, specify @- as an option to lnsc and pass the .lns file paths in stdin. The .lns file path passed to stdin will output one file per line.

For example, to pass all .lns files from the proj directory onwards:

$ find proj -name '*.lns' | lnsc @- save

This is currently the fastest method.

Reference information

The following information is for reference only.

meta information file

A meta information file is a file that summarizes information such as the classes published by a module and the dependencies of other modules that the module imports.

By reading this meta information file instead of parsing the module when importing, parsing time can be reduced.

A meta information file can be generated with the following command:

$ lnsc hello.lns SAVE

What is different from the previous command is the difference between "save" and "SAVE".

Lowercase save will generate only transcompiled Lua code, while uppercase SAVE will generate Lua code and meta information files.

Specifically, the above command will generate hello.lua and hello.meta.

LuneScript will load the .meta file when all of the following conditions are met when importing the module. At this time, the .lns file to be imported is not analyzed.

  • .lns has corresponding .lua and .meta
  • The update time of each file satisfies the following conditions

    • .lns < .meta
  • The following conditions are satisfied for the update time of module mod2 that is imported in mod1.lns to be imported.

    • mod1 > mod2
  • The format version of the .meta file is correct

dependencies

When there are two modules mod1 and mod2, if mod1 imports mod2, then "mod1 depends on mod2".

"mod1 depends on mod2" means that when mod2 is modified, "not only mod2 but also mod1 needs to be transcompiled".

The make command has long been used to build modules with such dependencies. There are various build tools in recent years, but there is no doubt that make is the representative build tool.

The make command controls updating files that depend on defined dependencies when a file is modified.

Defining this dependency manually can be rather tedious.

LuneScript provides the ability to auto-generate dependencies for the make command.

To automatically generate dependencies with LuneScript, specify options when SAVE as follows.

$ lnsc mod1.lns --depends depends/mod1.d SAVE

This will both transcompile mod1.lns and print the dependency information for mod1.lns in depends/mod1.d .

By incorporating this dependency information into the Makefile, you can easily control builds with make without manually defining dependencies.

sample

For example, let's say you have a project with the following modules:

test/proj/
      |
      +--- Mod1.lns
      |
      +--- Mod2.lns
      |
      +--- Mod3.lns
      |
      +--- Mod4.lns

Here are the contents of each file:

  • Mod1.lns
// @lnsFront: skip
import test.proj.Mod2;

pub fn func(): str {
   return "%s -> %s" (__func__, Mod2.func() );
}
print( func() );
  • Mod2.lns
// @lnsFront: skip
import test.proj.Mod3;

pub fn func(): str {
   return "%s -> %s" (__func__, Mod3.func() );
}
  • Mod3.lns
// @lnsFront: skip
import test.proj.Mod4;

pub fn func(): str {
   return "%s -> %s" (__func__, Mod4.func() );
}
  • Mod4.lns
// @lnsFront: ok
pub fn func(): str {
   return __func__;
}

The dependencies for the above file are:

File dependent file
Mod1.lns Mod2.lns
Mod2.lns Mod3.lns
Mod3.lns Mod4.lns
Mod4.lns none

The Makefile that builds this project looks like this:

PROJ_DIR=test/proj
MKFILE=$(PROJ_DIR)/Makefile
SRC_DIR=$(PROJ_DIR)/

.PHONY: test all build setup

define comp
	@echo "$1 -> $2"
	lnsc $1 --depends depends/$(shell echo $1 | sed 's@/@.@g').d SAVE
endef

%.meta: %.lns
	$(call comp,$<,$@)

SRCS =
SRCS += Mod1.lns
SRCS += Mod2.lns
SRCS += Mod3.lns
SRCS += Mod4.lns

SRCS := $(addprefix $(SRC_DIR),$(SRCS))

META_LIST=$(SRCS:.lns=.meta)
LUA_LIST=$(SRCS:.lns=.lua)

-include depends/*.d

all:
	@echo make setup
	@echo make build

setup:
	mkdir -p depends

build: $(META_LIST)

The important part here is define comp and -include depends/*.d.

  • define comp has registered the process of transcompiling and generating dependency information files.
  • -include depends/*.d is reading the generated dependency information file.

By creating such a makefile, it is possible to build according to import dependencies.

summary

Build control for LuneScript projects can be easily achieved using the make command.