[Build] Updated syntax for Rogue 2

This commit is contained in:
Abe Pralle 2022-09-09 11:28:51 -10:00
parent 52f004b8d9
commit 6eedd13b52
1 changed files with 124 additions and 71 deletions

View File

@ -1,6 +1,6 @@
# To run this build file, install Rogue from github.com/AbePralle/Rogue and type "rogo" at the command line. # To run this build file, install Rogue from github.com/AbePralle/Rogue and type "rogo" at the command line.
#$ LIBRARIES(macOS) = rgbds( which:rgbasm ) #$ LIBRARIES(macOS) = rgbds( exe:rgbasm )
# description()s are optional - Rogo uses introspection to determine which commands are available. # description()s are optional - Rogo uses introspection to determine which commands are available.
# 'rogo help default' displays the description for "default", etc. # 'rogo help default' displays the description for "default", etc.
@ -18,30 +18,30 @@ routine rogo_default
endRoutine endRoutine
routine rogo_build routine rogo_build
local rgbasm = System.find_program( "rgbasm" ) local rgbasm = System.find_executable( "rgbasm" )
if (not rgbasm) if (not rgbasm)
throw Error( "Please install the RGBDS Game Boy assembler from https://github.com/rednex/rgbds" ) throw Error( "Please install the RGBDS Game Boy assembler from https://github.com/rednex/rgbds" )
endIf endIf
File.create_folder( "Build" ) File( "Build" ).create_folder
File.create_folder( "ROM" ) File( "ROM" ).create_folder
local build_output = "Build/" + Build.ROM_NAME local build_output = "Build/" + Build.ROM_NAME
local rom_output = "ROM/" + Build.ROM_NAME local rom_output = "ROM/" + Build.ROM_NAME
local newest_datafile_timestamp = 0.0 local newest_datafile_timestamp = 0.0
forEach (datafile in File.listing("Data/**",&files,&ignore_hidden)) forEach (datafile in File("Data/**").listing(&files,&ignore_hidden))
newest_datafile_timestamp = newest_datafile_timestamp.or_larger( File.timestamp(datafile) ) newest_datafile_timestamp = newest_datafile_timestamp.or_larger( File(datafile).timestamp_ms )
endForEach endForEach
local obj_files = String[] local obj_files = String[]
local any_new_obj_files = false local any_new_obj_files = false
forEach (asm_file in File.listing("Source/**/*.asm")) forEach (asm_file in File("Source/**/*.asm").listing)
local obj_file = "Build/$.obj" (File.filename(asm_file).leftmost(-4)) local obj_file = "Build/$.obj" (File(asm_file).filename.leftmost(-4))
if (File.is_newer_than(asm_file,obj_file) or newest_datafile_timestamp > File.timestamp(obj_file)) if (File(asm_file).is_newer_than(obj_file) or newest_datafile_timestamp > File(obj_file).timestamp_ms)
execute "rgbasm -p 0xff $ -o $" (asm_file,obj_file) execute "rgbasm -p 0xff $ -o $" (asm_file,obj_file)
any_new_obj_files = true any_new_obj_files = true
elseIf (File.is_newer_than(obj_file,rom_output)) elseIf (File(obj_file).is_newer_than(rom_output))
any_new_obj_files = true any_new_obj_files = true
endIf endIf
obj_files.add( obj_file ) obj_files.add( obj_file )
@ -51,11 +51,11 @@ routine rogo_build
execute "rgblink --map Build/FGB.map --sym Build/FGB.sym $ -o $" (obj_files.join(" "),build_output) execute "rgblink --map Build/FGB.map --sym Build/FGB.sym $ -o $" (obj_files.join(" "),build_output)
execute "rgbfix -p 0xff -v $" (build_output) execute "rgbfix -p 0xff -v $" (build_output)
local file_size = File.size( build_output ) local file_size = File( build_output ).size
println "> Copy $ -> $ ($ bytes)" (build_output,rom_output,file_size.format(",")) println "> Copy $ -> $ ($ bytes)" (build_output,rom_output,file_size.format(","))
File.copy( build_output, rom_output ) File( build_output ).copy_to( rom_output )
else else
println "No changes detected. Output: $ ($ bytes)" (rom_output,File.size(rom_output).format(",")) println "No changes detected. Output: $ ($ bytes)" (rom_output,File(rom_output).size.format(","))
endIf endIf
endRoutine endRoutine
@ -64,57 +64,77 @@ routine rogo_clean
endRoutine endRoutine
routine verbose_delete( filepath:String ) routine verbose_delete( filepath:String )
if (File.exists(filepath)) if (File(filepath).exists)
println "> Delete " + filepath println "> Delete " + filepath
File.delete( filepath ) if (not File(filepath).delete)
println "*** Failed to delete - retrying with sudo"
local cmd = ''sudo rm -rf $'' (File(filepath).esc)
execute cmd
endIf
endIf endIf
endRoutine endRoutine
routine execute( commands:String, &suppress_error )->Logical routine execute( commands:String, error_message=null:String, &suppress_error )->Logical
forEach (cmd in LineReader(commands)) forEach (cmd in LineReader(commands))
print( "> " ).println( cmd ) print( "> " )
if (System.run(cmd) != 0) println( cmd )
if (suppress_error) return false if (0 != System.run(cmd))
else throw Error( "Build failed." ) if (suppress_error)
return false
else
if (not error_message) error_message = "Build failed."
throw Error( error_message )
endIf
endIf endIf
endForEach endForEach
return true return true
endRoutine endRoutine
#------------------------------------------------------------------------------ #-------------------------------------------------------------------------------
# Introspection-based Launcher Framework # Introspection-based Launcher Framework
#------------------------------------------------------------------------------ #-------------------------------------------------------------------------------
# Rogo is a "build your own build system" facilitator. At its core Rogo just recompiles build files if # Rogo is a "build your own build system" facilitator. At its core Rogo just
# needed and then runs the build executable while forwarding any command line arguments. This file contains # recompiles build files if needed and then runs the build executable while
# a default framework which uses introspection to turn command line arguments into parameterized routine calls. # 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 )". # 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. # "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 # The following "comment directives" can be used in this file to control how
# manage automatic dependency installation and linking. # 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). # Each of the following should be on a line beginning with the characters #$
# Sample args are given. # (preceding whitespace is fine). Sample args are given.
# ROGUEC = roguec # Path to roguec compiler to compile this file with # ROGUEC = roguec # Path to roguec to compile this file with
# ROGUEC_ARGS = --whatever # Additional options to pass to RogueC # 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 # CC = gcc -Wall -fno-strict-aliasing
# CPP_ARGS = -a -b -c # Additional C++ args # CC_ARGS = -a -b -c # Additional C args
# LINK = true # Links following LIBRARIES with this Build file # LINK = -lalpha -lbeta # Link this build file with these options
# LINK = -lalpha -lbeta # Links following LIBRARIES and includes these additional flags # LINK(macOS) = ... # Options applying only to
# LINK = false # Linking turned off for following LIBRARIES - info can still be obtained from #$LIBRARY_FLAGS() # # System.os=="macOS" (use with any OS and
# LINK(macOS) = ... # Options applying only to System.os=="macOS" (use with any OS and any comment directive) # # any comment directive)
# LINK_LIBS = true # Links following LIBRARIES with this Build
# # file (otherwise just installs them)
# LINK_LIBS = false # Linking turned off for following
# # LIBRARIES - info can still be obtained
# # from $LIBRARY_FLAGS or $LIBRARIES(libname,...)
# LIBRARIES = libalpha # LIBRARIES = libalpha
# LIBRARIES = libbeta(library-name) # LIBRARIES = libbeta(library-name)
# LIBRARIES = libfreetype6-dev(freetype2) # LIBRARIES = libfreetype6-dev(freetype2)
# DEPENDENCIES = Library/Rogue/**/*.rogue # DEPENDENCIES = Library/Rogue/**/*.rogue
# #
# LIBRARIES = name(package) # LIBRARIES = name(package)
# LIBRARIES = name(package:<package> install:<install-cmd> link:<link-flags> which:<which-name>) # 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: # The following macro is replaced within this file (Build.rogue) - the libraries
# should normally also be declared in #$ LIBRARIES:
# #
# $LIBRARY_FLAGS(lib1,lib2) # sample macro # $LIBRARY_FLAGS(lib1,lib2) # sample macro
# -> # ->
@ -128,9 +148,9 @@ routine description( command:String, text:String )
Build.rogo_descriptions[ command ] = text Build.rogo_descriptions[ command ] = text
endRoutine endRoutine
routine help( command:String, description=null:String, syntax=null:String ) routine help( command:String, description_text=null:String, syntax_text=null:String )
if (description) Global.description( command, description ) if (description_text) description( command, description_text )
if (syntax) Global.syntax( command, syntax ) if (syntax_text) syntax( command, syntax_text )
endRoutine endRoutine
try try
@ -142,14 +162,14 @@ endTry
class Build [singleton] class Build [singleton]
PROPERTIES PROPERTIES
rogo_syntax = StringTable<<String>>() rogo_syntax = [String:String]
rogo_descriptions = StringTable<<String>>() rogo_descriptions = [String:String]
rogo_prefix = ?:{ $moduleName.count:$moduleName "::" || "" } + "rogo_" : String rogo_prefix = "rogo_"
rogo_command = "default" rogo_command = "default"
rogo_args = @[] rogo_args = @[]
rogo_error : Error rogo_error : Error
LOCAL_DEFS_FILE = "Local.mk" LOCAL_SETTINGS_FILE = "Local.settings"
METHODS METHODS
method launch method launch
@ -161,27 +181,27 @@ class Build [singleton]
method dispatch_command method dispatch_command
local m = find_command( rogo_command ) local m = find_command( rogo_command )
require m || "no such routine rogo_$()" (rogo_command) if (not m) throw Error( "No such routine rogo_$()" (rogo_command) )
local args = @[] local args = @[]
forEach (arg in rogo_args) forEach (arg in rogo_args)
which (arg) which (arg)
case "true": args.add( true ) case "true": args.add( true )
case "false": args.add( false ) case "false": args.add( false )
case "null": args.add( NullValue ) case "null": args.add( null )
others: args.add( arg ) others: args.add( arg )
endWhich endWhich
endForEach endForEach
if (m.parameter_count == 1 and args.count > 1) args = @[ args ] # Wrap args in a ValueList.
m( args ) m( args )
method find_command( name:String )->MethodInfo method find_command( name:String )->MethodInfo
return <<Global>>.find_global_method( rogo_prefix + name ) return <<Routine>>.find_global_method( rogo_prefix + name )
method on_error method on_error
Console.error.println "=" * 79 local w = Console.width.or_smaller( 80 )
Console.error.println "=" * w
Console.error.println rogo_error Console.error.println rogo_error
Console.error.println "=" * 79 Console.error.println "=" * w
on_exit on_exit
System.exit 1 System.exit 1
@ -189,9 +209,10 @@ class Build [singleton]
noAction noAction
method on_command_not_found method on_command_not_found
println "=" * 79 local w = Console.width.or_smaller( 80 )
println "=" * w
println "ERROR: No such command '$'." (rogo_args.first) println "ERROR: No such command '$'." (rogo_args.first)
println "=" * 79 println "=" * w
println println
rogo_command = "help" rogo_command = "help"
rogo_args.clear rogo_args.clear
@ -211,7 +232,7 @@ class Build [singleton]
rogo_args.clear rogo_args.clear
while (parts.count) while (parts.count)
local cmd = _join( parts ) local cmd = parts.join("_")
if (find_command(cmd)) if (find_command(cmd))
rogo_command = cmd rogo_command = cmd
on_command_found on_command_found
@ -228,12 +249,12 @@ class Build [singleton]
endBlock endBlock
method read_defs method read_defs
read_defs( LOCAL_DEFS_FILE ) read_defs( LOCAL_SETTINGS_FILE )
method read_defs( defs_filepath:String ) method read_defs( defs_filepath:String )
# Attempt to read defs from Local.mk # Attempt to read defs from Local.settings
local overrides = String[] local overrides = String[]
if (File.exists(defs_filepath)) if (File(defs_filepath).exists)
forEach (line in LineReader(File(defs_filepath))) forEach (line in LineReader(File(defs_filepath)))
if (line.contains("=")) if (line.contains("="))
local name = line.before_first('=').trimmed local name = line.before_first('=').trimmed
@ -244,7 +265,7 @@ class Build [singleton]
local p = <<Build>>.find_property( name ) local p = <<Build>>.find_property( name )
if (p) if (p)
overrides.add( "$ = $" (name,value) ) overrides.add( "$ = $" (name,value) )
<<Build>>.set_property( this, p, Value(value) ) p.set_value( this, value )
endIf endIf
endIf endIf
endForEach endForEach
@ -258,6 +279,8 @@ endClass
routine rogo_help( command="":String ) routine rogo_help( command="":String )
# SYNTAX: rogo help [command]
# Displays help for a specified command or else all build commands.
command = Build._join( Build.rogo_args ) command = Build._join( Build.rogo_args )
if (command.count) if (command.count)
local syntax = get_syntax( command ) local syntax = get_syntax( command )
@ -270,9 +293,11 @@ routine rogo_help( command="":String )
endIf endIf
local description = get_description( command ) local description = get_description( command )
if (description) if (description)
local max_w = Console.width - 2
println "DESCRIPTION" println "DESCRIPTION"
forEach (line in LineReader(description.word_wrapped(76))) forEach (line in LineReader(description.word_wrapped(max_w)))
print( " " ).println( line ) print( " " )
println( line )
endForEach endForEach
println println
success = true success = true
@ -280,34 +305,60 @@ routine rogo_help( command="":String )
if (success) if (success)
return return
else else
println "=" * 79 local w = Console.width.or_smaller( 80 )
println "=" * w
println "ERROR: No such command '$'." (command) println "ERROR: No such command '$'." (command)
println "=" * 79 println "=" * w
println println
endIf endIf
endIf endIf
println "USAGE" println "USAGE"
local lines = String[] local entries = CommandInfo[]
forEach (m in <<Global>>.global_methods) local max_len = 0
forEach (m in <<Routine>>.global_methods)
if (m.name.begins_with(Build.rogo_prefix)) if (m.name.begins_with(Build.rogo_prefix))
lines.add( " " + get_syntax(m.name.after_first(Build.rogo_prefix)) ) local name = m.name.after_first( Build.rogo_prefix )
local entry = CommandInfo( name, get_syntax(name), get_description(name) )
max_len .= or_larger( entry.syntax.count )
entries.add entry
endIf endIf
endForEach endForEach
lines.sort( (a,b)=>(a<b) )
println (forEach in lines) entries.sort( $1.name < $2.name )
max_len += 2
local max_w = Console.width
forEach (entry in entries)
print " " + entry.syntax
if (entry.@description)
local description = entry.@description
loop (max_len - entry.syntax.count) print ' '
contingent
sufficient (max_len + description.count <= max_w)
if (description.contains(". "))
description = description.before_first( ". " ) + "."
sufficient (max_len + description.count <= max_w)
endIf
necessary (max_len + 10 <= max_w)
description = description.unright( (description.count - (max_w - max_len))+3 ) + "..."
satisfied
print description
endContingent
endIf
println
endForEach
println println
endRoutine endRoutine
routine get_syntax( m_name:String )->String routine get_syntax( m_name:String )->String
if (Build.rogo_syntax.contains(m_name)) if (Build.rogo_syntax.contains(m_name))
return "rogo " + Build.rogo_syntax[ m_name ] return "rogo " + Build.rogo_syntax[ m_name ]
else else
local m = <<Global>>.find_global_method( Build.rogo_prefix + m_name ) local m = <<Routine>>.find_global_method( Build.rogo_prefix + m_name )
if (not m) return null if (not m) return null
local line = "rogo $" (m_name.replacing('_',' ')) local line = "rogo $" (m_name.replacing('_',' '))
line += " <$>" (m.parameter_name(forEach in 0..<m.parameter_count)) line += " <$>" ((forEach in m.parameters).name)
return line return line
endIf endIf
endRoutine endRoutine
@ -320,3 +371,5 @@ routine get_description( m_name:String )->String
return null return null
endIf endIf
endRoutine endRoutine
class CommandInfo( name:String, syntax:String, description:String );