AbePralle-FGB/Build.rogue

323 lines
10 KiB
Plaintext

# To run this build file, install Rogue from github.com/AbePralle/Rogue and type "rogo" at the command line.
#$ LIBRARIES(macOS) = rgbds( which:rgbasm )
# description()s are optional - Rogo uses introspection to determine which commands are available.
# 'rogo help default' displays the description for "default", etc.
description( "default", "The default action is performed when no other actions are specified. Use 'rogo default' to explicitly perform the default option." )
description( "help", "Displays a list of all actions that can be performed by Rogo." )
augment Build
PROPERTIES
# These properties can be overridden with a Local.mk setting ROM_NAME=OtherName etc.
ROM_NAME = "FGB.gb"
endAugment
routine rogo_default
rogo_build
endRoutine
routine rogo_build
local rgbasm = System.find_program( "rgbasm" )
if (not rgbasm)
throw Error( "Please install the RGBDS Game Boy assembler from https://github.com/rednex/rgbds" )
endIf
File.create_folder( "Build" )
File.create_folder( "ROM" )
local build_output = "Build/" + Build.ROM_NAME
local rom_output = "ROM/" + Build.ROM_NAME
local newest_datafile_timestamp = 0.0
forEach (datafile in File.listing("Data/**",&files,&ignore_hidden))
newest_datafile_timestamp = newest_datafile_timestamp.or_larger( File.timestamp(datafile) )
endForEach
local obj_files = String[]
local any_new_obj_files = false
forEach (asm_file in File.listing("Source/**/*.asm"))
local obj_file = "Build/$.obj" (File.filename(asm_file).leftmost(-4))
if (File.is_newer_than(asm_file,obj_file) or newest_datafile_timestamp > File.timestamp(obj_file))
execute "rgbasm -p 0xff $ -o $" (asm_file,obj_file)
any_new_obj_files = true
elseIf (File.is_newer_than(obj_file,rom_output))
any_new_obj_files = true
endIf
obj_files.add( obj_file )
endForEach
if (any_new_obj_files)
execute "rgblink --map Build/FGB.map --sym Build/FGB.sym $ -o $" (obj_files.join(" "),build_output)
execute "rgbfix -p 0xff -v $" (build_output)
local file_size = File.size( build_output )
println "> Copy $ -> $ ($ bytes)" (build_output,rom_output,file_size.format(","))
File.copy( build_output, rom_output )
else
println "No changes detected. Output: $ ($ bytes)" (rom_output,File.size(rom_output).format(","))
endIf
endRoutine
routine rogo_clean
verbose_delete( "Build" )
endRoutine
routine verbose_delete( filepath:String )
if (File.exists(filepath))
println "> Delete " + filepath
File.delete( filepath )
endIf
endRoutine
routine execute( commands:String, &suppress_error )->Logical
forEach (cmd in LineReader(commands))
print( "> " ).println( cmd )
if (System.run(cmd) != 0)
if (suppress_error) return false
else throw Error( "Build failed." )
endIf
endForEach
return true
endRoutine
#------------------------------------------------------------------------------
# Introspection-based Launcher Framework
#------------------------------------------------------------------------------
# Rogo is a "build your own build system" facilitator. At its core Rogo just recompiles build files if
# needed and then runs the build executable while forwarding any command line arguments. This file contains
# a default framework which uses introspection to turn command line arguments into parameterized routine calls.
# Example: to handle the command "rogo abc xyz 5", define "routine rogo_abc_xyz( n:Int32 )".
# "rogo_default" will run in the absence of any other command line argument.
# The following "comment directives" can be used in this file to control how RogueC compiles it and to
# manage automatic dependency installation and linking.
# Each of the following should be on a line beginning with the characters #$ (preceding whitespace is fine).
# Sample args are given.
# ROGUEC = roguec # Path to roguec compiler to compile this file with
# ROGUEC_ARGS = --whatever # Additional options to pass to RogueC
# CPP = g++ -Wall -std=gnu++11 -fno-strict-aliasing -Wno-invalid-offsetof # C++ compiler path and/or invocation
# CPP_ARGS = -a -b -c # Additional C++ args
# LINK = true # Links following LIBRARIES with this Build file
# LINK = -lalpha -lbeta # Links following LIBRARIES and includes these additional flags
# LINK = false # Linking turned off for following LIBRARIES - info can still be obtained from #$LIBRARY_FLAGS()
# LINK(macOS) = ... # Options applying only to System.os=="macOS" (use with any OS and any comment directive)
# LIBRARIES = libalpha
# LIBRARIES = libbeta(library-name)
# LIBRARIES = libfreetype6-dev(freetype2)
# DEPENDENCIES = Library/Rogue/**/*.rogue
#
# LIBRARIES = name(package)
# LIBRARIES = name(package:<package> install:<install-cmd> link:<link-flags> which:<which-name>)
#
# The following macro is replaced within this file (Build.rogue) - the libraries should also be declared in #$ LIBRARIES:
#
# $LIBRARY_FLAGS(lib1,lib2) # sample macro
# ->
# -Ipath/to/lib1/include -Lpath/to/lib1/library -I ... # sample replacement
routine syntax( command:String, text:String )
Build.rogo_syntax[ command ] = text
endRoutine
routine description( command:String, text:String )
Build.rogo_descriptions[ command ] = text
endRoutine
routine help( command:String, description=null:String, syntax=null:String )
if (description) Global.description( command, description )
if (syntax) Global.syntax( command, syntax )
endRoutine
try
Build.launch
catch (err:Error)
Build.rogo_error = err
Build.on_error
endTry
class Build [singleton]
PROPERTIES
rogo_syntax = StringTable<<String>>()
rogo_descriptions = StringTable<<String>>()
rogo_prefix = ?:{ $moduleName.count:$moduleName "::" || "" } + "rogo_" : String
rogo_command = "default"
rogo_args = @[]
rogo_error : Error
LOCAL_DEFS_FILE = "Local.mk"
METHODS
method launch
rogo_args.add( forEach in System.command_line_arguments )
read_defs
on_launch
parse_args
dispatch_command
method dispatch_command
local m = find_command( rogo_command )
require m
local args = @[]
forEach (arg in rogo_args)
which (arg)
case "true": args.add( true )
case "false": args.add( false )
case "null": args.add( NullValue )
others: args.add( arg )
endWhich
endForEach
if (m.parameter_count == 1 and args.count > 1) args = @[ args ] # Wrap args in a ValueList.
m.call( Global, args )
method find_command( name:String )->MethodInfo
return <<Global>>.find_method( rogo_prefix + name )
method on_error
Console.error.println "=" * 79
Console.error.println rogo_error
Console.error.println "=" * 79
on_exit
System.exit 1
method on_command_found
noAction
method on_command_not_found
println "=" * 79
println "ERROR: No such command '$'." (rogo_args.first)
println "=" * 79
println
rogo_command = "help"
rogo_args.clear
on_command_found
method on_launch
noAction
method on_exit
noAction
method parse_args
block
if (rogo_args.count)
local parts = String[]
parts.add( forEach in rogo_args )
rogo_args.clear
while (parts.count)
local cmd = _join( parts )
if (find_command(cmd))
rogo_command = cmd
on_command_found
escapeBlock
endIf
rogo_args.insert( parts.remove_last )
endWhile
on_command_not_found
endIf
# Use default command
on_command_found
endBlock
method read_defs
read_defs( LOCAL_DEFS_FILE )
method read_defs( defs_filepath:String )
# Attempt to read defs from Local.mk
local overrides = String[]
if (File.exists(defs_filepath))
forEach (line in LineReader(File(defs_filepath)))
if (line.contains("="))
local name = line.before_first('=').trimmed
local value = line.after_first('=').trimmed
if (value.begins_with('"') or value.begins_with('\''))
value = value.leftmost(-1).rightmost(-1)
endIf
local p = <<Build>>.find_property( name )
if (p)
overrides.add( "$ = $" (name,value) )
<<Build>>.set_property( this, p, Value(value) )
endIf
endIf
endForEach
endIf
method _join( value:Value )->String
local args = String[]
args.add( forEach in value )
return args.join( "_" )
endClass
routine rogo_help( command="":String )
command = Build._join( Build.rogo_args )
if (command.count)
local syntax = get_syntax( command )
local success = false
if (syntax)
println "SYNTAX"
println " " + syntax
println
success = true
endIf
local description = get_description( command )
if (description)
println "DESCRIPTION"
forEach (line in LineReader(description.word_wrapped(76)))
print( " " ).println( line )
endForEach
println
success = true
endIf
if (success)
return
else
println "=" * 79
println "ERROR: No such command '$'." (command)
println "=" * 79
println
endIf
endIf
println "USAGE"
local lines = String[]
forEach (m in <<Global>>.methods)
if (m.name.begins_with(Build.rogo_prefix))
lines.add( " " + get_syntax(m.name.after_first(Build.rogo_prefix)) )
endIf
endForEach
lines.sort( (a,b)=>(a<b) )
println (forEach in lines)
println
endRoutine
routine get_syntax( m_name:String )->String
if (Build.rogo_syntax.contains(m_name))
return "rogo " + Build.rogo_syntax[ m_name ]
else
local m = <<Global>>.find_method( Build.rogo_prefix + m_name )
if (not m) return null
local line = "rogo $" (m_name.replacing('_',' '))
line += " <$>" (m.parameter_name(forEach in 0..<m.parameter_count))
return line
endIf
endRoutine
routine get_description( m_name:String )->String
if (Build.rogo_descriptions.contains(m_name))
return Build.rogo_descriptions[ m_name ]
else
return null
endIf
endRoutine