Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Run arbitrary code at compile time

I know that Crystal has its macro system for defining code at compile time but is it possible to run code apart from that?

For example, can we do this so that it runs during compilation?

puts "foobar"

Or for a more serious example, can we read from and write to the file system where the compiler is running?

like image 292
Lye Fish Avatar asked Dec 03 '25 09:12

Lye Fish


2 Answers

Yes we can! With the help of the run macro method or the system macro method.

Let's have an example that compiles a random greeting into the program each time it is build:

greetings.txt:

Hello
Hey
Hi

greeting.cr:

puts File.read_lines("#{__DIR__}/greetings.txt").sample

greeter.cr:

puts {{run("./greeting").stringify}}

Compile with crystal build greeter.cr, you'll notice that the output stays the same for the compiled binary but is random for each time your recompile it.

like image 144
Jonne Haß Avatar answered Dec 06 '25 21:12

Jonne Haß


As an additional answer to what Jonne said, you can output something at compile time using puts, but inside a macro. For example:

{{ puts "foobar" }}

You'll notice that "foobar" is printed during compilation but the executable does nothing (try it with crystal build foo.cr; ./foo)

Of course just outputting something at compile time isn't very useful, but it's useful when you want to debug some macros in a quick way.

The puts macro method is documented here: http://crystal-lang.org/api/Macros.html#puts%28expression%29%3ANop-instance-method

like image 25
asterite Avatar answered Dec 06 '25 20:12

asterite