Using Rake to compile FreeBASIC code


#1

Hello list!,

Maybe the subject sound strange, but FreeBASIC
(http://www.freebasic.net) offer us native assembly-linked code that we
currently use to drive a few special purpose dlls on windows.

Folling Rake Tutorial, we tried to recreate it for fb (instead of c).

The problem is related in the way FB set the main module on the compiled
object (an special param must be passed to make the module as main).

In the later link process, if no module is defined/marked as main, the
link stop and raise a undefined main routine.

We solved this creating an Array of “MAINS”, when the compile rule find
the module in the list, add the param for making it main.

The problem is that in our Rakefile we have several projects, and some
of them use these modules but not as main, so it raises a problem of
multiple definitions.

There is the code we are using:

MAINS = []
LIBS = []

rule ‘.o’ => ‘.bas’ do |t|
fbc_compile t
end

def fb_project(executable, main, files, libs=nil)
obj = FileList.new(files)
obj.include(main)
MAINS << main

obj.each { |fn| fn.sub!(/.bas$/, “.o”) }

file executable => obj do |t|
fbc_link t, libs
end
end

def fbc_compile(task)
as_main = “-m #{task.name.chomp(’.o’)}” if MAINS.include?(task.name)
sh “fbc -c #{task.source} -o #{task.name} #{as_main}”
end

def fbc_link(task, libs)
lb = []
libs.each { |libname| lb << “-l #{libname}” } unless libs.nil?

sh “fbc -x #{task.name}.exe #{task.prerequisites.join(’ ‘)}
#{lb.join(’ ')}”
end

fb_project(“test”, “stub.bas”, ["**/.bas", '.rc’], [“user32”])
task :compile => :test

fb_project(“xx”, “module1.bas”, [“module2.bas”], [“user32”])
task :compile => :xx

If we call test or xx task (or even compile) module1.bas and stub.bas
both get compiled “as main”, and the link process fail.

Any suggestion or approach to solve this issue?

Thanks in advance,

Luis


#2

Luis L. wrote:

Maybe the subject sound strange, but FreeBASIC
(http://www.freebasic.net) offer us native assembly-linked code that we
currently use to drive a few special purpose dlls on windows.

Folling Rake Tutorial, we tried to recreate it for fb (instead of c).

Cool!

The problem is related in the way FB set the main module on the compiled
object (an special param must be passed to make the module as main).

In the later link process, if no module is defined/marked as main, the
link stop and raise a undefined main routine.
[…]
The problem is that in our Rakefile we have several projects, and some
of them use these modules but not as main, so it raises a problem of
multiple definitions.

Ok, the problem (as I understand it) is that a main module must be
specially compiled. Only one main module can be in a executable. And
modules that are main for one program need to be non-main for other
executables.

Does that sound right?

I approached with the thought that modules that could be main must be
compiled twice, once normally for use as a library, and once with the
main flag to be used as the main program.

I setup two rules to handle that. The first rule is similar to the one
you gave in your Rakefile. It only compiles normal modules.

The second rule is for generating objects in a special directory (e.g.
“mains”). All object modules in the “mains” directory have been
compiled for use as a main.

Then all I had to do was have the project method excluded the normal .o
file that corresponds to the main module and include the specially
compiled version in the main directory.

Just a couple notes. I don’t have FreeBasic so I couldn’t actually
compile anything. I just simulated the fbc program with a simple script
that just touched the output files. This means I may have gotten some
of the details wrong, especially the flags that are sent to the compile.
In particular, double check the -m flag. I assume that it wants the
name of the module without any directory name or file extension.

Here’s my version … let me know of this works for you:


#!/usr/bin/env ruby

-- ruby --

require ‘rake/clean’

CLEAN.include ‘**/.o’, ‘mains’
CLOBBER.include '
.exe’

LIBS = []

directory ‘mains’

Rule to build normal objects from source.

rule ‘.o’ => ‘.bas’ do |t|
fbc_compile t.source, t.name
end

Rule to compile main objects from source.

rule %r{^mains/.*.o$} => [
proc { |fn| File.basename(fn).ext(‘bas’) },
proc { ‘mains’ }
] do |t|
fbc_main_compile t, t.source
end

def fb_project(executable, main, files, libs=[])
bas_files = FileList[*files.grep(/.bas$/)]
rc_files = FileList[*files.grep(/.rc$/)]
objs = bas_files.ext(“o”) + rc_files
objs.exclude(main.ext(“o”))
objs.include(File.join(“mains”, main.ext(“o”)))

task :compile => executable
task executable => executable.ext(“exe”)
file executable.ext(“exe”) => objs do |t|
fbc_link t.name, t.prerequisites, libs
end
end

def fbc_compile(src, obj)
sh “fbc -c #{src} -o #{obj}”
end

def fbc_main_compile(task, main_name)
sh “fbc -c #{task.source} -o #{task.name} -m #{main_name.ext}”
end

def fbc_link(exec, deps, libs)
lb = libs.collect { |lib| “-l #{lib}” }.join(’ ‘)
sh "fbc -x #{exec} #{deps.join(’ ')} #{lb}"
end

fb_project(“test”, “stub.bas”, ["**/.bas", '.rc’], [“user32”])

fb_project(“xx”, “module1.bas”, [“module2.bas”], [“user32”])

task :default => :compile


– Jim W.


#3

Jim W. wrote:

Luis L. wrote:

Maybe the subject sound strange, but FreeBASIC
(http://www.freebasic.net) offer us native assembly-linked code that we
currently use to drive a few special purpose dlls on windows.

Folling Rake Tutorial, we tried to recreate it for fb (instead of c).

Cool!

In one of our projects, the business logic is driven by a embedded lua
interpreter (called and extended from FB), that way we provide simpler
access to change logic without need of recompiling.

We are using RoR and wanted to take advantage of Rake for our libraries
too (we are tired of batch files!)

Ok, the problem (as I understand it) is that a main module must be
specially compiled. Only one main module can be in a executable. And
modules that are main for one program need to be non-main for other
executables.

Does that sound right?

Yes, that is correct, please excuse my bad grammar (self learned
english, spanish native speaker).

I approached with the thought that modules that could be main must be
compiled twice, once normally for use as a library, and once with the
main flag to be used as the main program.

[snip]

Just a couple notes. I don’t have FreeBasic so I couldn’t actually
compile anything. I just simulated the fbc program with a simple script
that just touched the output files. This means I may have gotten some
of the details wrong, especially the flags that are sent to the compile.
In particular, double check the -m flag. I assume that it wants the
name of the module without any directory name or file extension.

Here’s my version … let me know of this works for you:

I really don’t know HOW you do this (trying to figure out the new rule
you added) but it works.

Yeah, copy & paste, run it and build everything without problems!

We just used ‘**/*.bas’ as example to include every module in the list,
doing that, for the “test” project, stub get compiled twice: one as
module, one as main.

But that is because we used magic wildcards, when in real-life,
wouldn’t.

Wanted to thank you for the time you took to answer me, and provide this
helpful solution.

Still learning ruby (a few months playing with it) but now I want to run
SWIG on the ruby headers to create compatible FB includes and create
native access (?) to freebasic (btw, its cross-platform win, linux, dos)

Also we created, unit testing ala UnitTest for FreeBASIC, TDD to the
max! :wink:

Regards and thank you again,

Luis


#4

Jim:

After toyed with the rakefile with simple files, moved it to my project.

my folder structure is something like this:

my_project
code/
shared/
test/
Rakefile

Inside code is where my main code resides, and shared is a
junction/symlink to where I store code for md5, blowfish, hash, list,
etc.

In test, I create a few test to “verify” each one of the units/modules
of my project.

Well, with the rakefile/rules you created in previous mail, making main
a module located inside another folder fails, with this error:

rake aborted!
Don’t know how to build task ‘mains/test/test_bf.o’

I have inside test a file named test_bf.bas

The task is named test_blowfish, so don’t “collide” between tasks

The rule for ‘.o’ => ‘.bas’ worked fine, but the one that makes “mains”
didn’t.

I tried to understand the regex rule, but fail to do so :frowning:

Any hint or suggestion to what need to tweak?

Regards and thank you for your time,

Luis

Jim W. wrote:

Luis L. wrote:

Maybe the subject sound strange, but FreeBASIC
(http://www.freebasic.net) offer us native assembly-linked code that we
currently use to drive a few special purpose dlls on windows.

Folling Rake Tutorial, we tried to recreate it for fb (instead of c).

Cool!

The problem is related in the way FB set the main module on the compiled
object (an special param must be passed to make the module as main).

In the later link process, if no module is defined/marked as main, the
link stop and raise a undefined main routine.
[…]
The problem is that in our Rakefile we have several projects, and some
of them use these modules but not as main, so it raises a problem of
multiple definitions.

Ok, the problem (as I understand it) is that a main module must be
specially compiled. Only one main module can be in a executable. And
modules that are main for one program need to be non-main for other
executables.

Does that sound right?

I approached with the thought that modules that could be main must be
compiled twice, once normally for use as a library, and once with the
main flag to be used as the main program.

I setup two rules to handle that. The first rule is similar to the one
you gave in your Rakefile. It only compiles normal modules.

The second rule is for generating objects in a special directory (e.g.
“mains”). All object modules in the “mains” directory have been
compiled for use as a main.

Then all I had to do was have the project method excluded the normal .o
file that corresponds to the main module and include the specially
compiled version in the main directory.

Just a couple notes. I don’t have FreeBasic so I couldn’t actually
compile anything. I just simulated the fbc program with a simple script
that just touched the output files. This means I may have gotten some
of the details wrong, especially the flags that are sent to the compile.
In particular, double check the -m flag. I assume that it wants the
name of the module without any directory name or file extension.

Here’s my version … let me know of this works for you:


#!/usr/bin/env ruby

-- ruby --

require ‘rake/clean’

CLEAN.include ‘**/.o’, ‘mains’
CLOBBER.include '
.exe’

LIBS = []

directory ‘mains’

Rule to build normal objects from source.

rule ‘.o’ => ‘.bas’ do |t|
fbc_compile t.source, t.name
end

Rule to compile main objects from source.

rule %r{^mains/.*.o$} => [
proc { |fn| File.basename(fn).ext(‘bas’) },
proc { ‘mains’ }
] do |t|
fbc_main_compile t, t.source
end

def fb_project(executable, main, files, libs=[])
bas_files = FileList[*files.grep(/.bas$/)]
rc_files = FileList[*files.grep(/.rc$/)]
objs = bas_files.ext(“o”) + rc_files
objs.exclude(main.ext(“o”))
objs.include(File.join(“mains”, main.ext(“o”)))

task :compile => executable
task executable => executable.ext(“exe”)
file executable.ext(“exe”) => objs do |t|
fbc_link t.name, t.prerequisites, libs
end
end

def fbc_compile(src, obj)
sh “fbc -c #{src} -o #{obj}”
end

def fbc_main_compile(task, main_name)
sh “fbc -c #{task.source} -o #{task.name} -m #{main_name.ext}”
end

def fbc_link(exec, deps, libs)
lb = libs.collect { |lib| “-l #{lib}” }.join(’ ‘)
sh "fbc -x #{exec} #{deps.join(’ ')} #{lb}"
end

fb_project(“test”, “stub.bas”, ["**/.bas", '.rc’], [“user32”])

fb_project(“xx”, “module1.bas”, [“module2.bas”], [“user32”])

task :default => :compile


– Jim W.


#5

Luis L. wrote:

Jim:

After toyed with the rakefile with simple files, moved it to my project.

my folder structure is something like this:

my_project
code/
shared/
test/
Rakefile

Inside code is where my main code resides, and shared is a
junction/symlink to where I store code for md5, blowfish, hash, list,
etc.

In test, I create a few test to “verify” each one of the units/modules
of my project.

Well, with the rakefile/rules you created in previous mail, making main
a module located inside another folder fails, with this error:

rake aborted!
Don’t know how to build task ‘mains/test/test_bf.o’

Ok, the rule needs to bridge the gap between test/test_bf.bas and
mains/test/test_bf.o. The regular expression part of the rule (the
r{^mains/.*.o$}) is fine, it should match “mains/test/test_bf.o”. The
list of procs are probably what’s wrong.

[ proc { |fn| File.basename(fn).ext(‘bas’) },
proc { ‘mains’ }
]

will produce the list:

[ ‘test_bf.bas’, ‘mains’ ]

which is wrong. What you want is:

[ ‘test/test_bf.bas’, ‘mains/test’ ]

So, just write two procs to produce that and put that in the rule.

Also, you will probably need to add a line that will describe how to
make the ‘mains/test’ directory (Just saying “directory ‘mains/test’” is
adequate).


– Jim W.


#6

Jim W. wrote:

Ok, the rule needs to bridge the gap between test/test_bf.bas and
mains/test/test_bf.o. The regular expression part of the rule (the
r{^mains/.*.o$}) is fine, it should match “mains/test/test_bf.o”. The
list of procs are probably what’s wrong.

[ proc { |fn| File.basename(fn).ext(‘bas’) },
proc { ‘mains’ }
]

will produce the list:

[ ‘test_bf.bas’, ‘mains’ ]

which is wrong. What you want is:

[ ‘test/test_bf.bas’, ‘mains/test’ ]

So, just write two procs to produce that and put that in the rule.

Also, you will probably need to add a line that will describe how to
make the ‘mains/test’ directory (Just saying “directory ‘mains/test’” is
adequate).

Thank you again Jim, following your indications I solved it (btw, this
use of proc is just like lamda, right?)

This is the new rule:

Rule to compile main objects from source.

rule %r{^mains/.*.o$} => [
proc { |fn| fn.sub(%r{^mains/}, ‘’).ext(‘bas’) },
proc { |fn| File.dirname(fn) }
] do |t|
sh “fbc -c #{t.source} -o #{t.name} -m #{File.basename(t.source).ext}”
end

Guess could be solved more “elegant”, but it works :slight_smile:

I added a directory task into fb_project to intelligently add when is
needed (mains/other or leave just mains)

Thank you for your time (again).

Regards,

Luis