2021-01-05 13:02:14 +01:00
#!/usr/bin/env python3
2020-12-26 20:05:45 +01:00
2020-11-02 20:06:52 +01:00
# Copyright 2020 Mattia Giambirtone
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# A minimalistic build script for JAPL
import os
import shlex
import logging
import argparse
from time import time
from typing import Dict
from pprint import pformat
from subprocess import Popen , PIPE , DEVNULL
2020-11-02 21:46:33 +01:00
2020-11-02 20:06:52 +01:00
CONFIG_TEMPLATE = ''' # Copyright 2020 Mattia Giambirtone
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import strformat
const MAP_LOAD_FACTOR * = { map_load_factor } # Load factor for builtin hashmaps (TODO)
const ARRAY_GROW_FACTOR * = { array_grow_factor } # How much extra memory to allocate for dynamic arrays (TODO)
const FRAMES_MAX * = { frames_max } # TODO: Inspect why the VM crashes if this exceeds this value
const JAPL_VERSION * = " 0.3.0 "
const JAPL_RELEASE * = " alpha "
const DEBUG_TRACE_VM * = { debug_vm } # Traces VM execution
const DEBUG_TRACE_GC * = { debug_gc } # Traces the garbage collector (TODO)
const DEBUG_TRACE_ALLOCATION * = { debug_alloc } # Traces memory allocation/deallocation (WIP)
const DEBUG_TRACE_COMPILER * = { debug_compiler } # Traces the compiler
const JAPL_VERSION_STRING * = & " JAPL {{ JAPL_VERSION}} ( {{ JAPL_RELEASE}}, {{ CompileDate}} {{ CompileTime}}) "
const HELP_MESSAGE * = """ The JAPL runtime interface, Copyright (C) 2020 Mattia Giambirtone
This program is free software , see the license distributed with this program or check
http : / / www . apache . org / licenses / LICENSE - 2.0 for more info .
Basic usage
- - - - - - - - - - -
$ jpl - > Start the REPL
$ jpl filename . jpl - > Run filename . jpl
Command - line options
- - - - - - - - - - - - - - - - - - - -
- h , - - help - > Show this help text and exit
- v , - - version - > Print the JAPL version number and exit
""" ' ' '
2020-11-02 21:37:57 +01:00
def build ( path : str , flags : Dict [ str , str ] = { } , options : Dict [ str , bool ] = { } , override : bool = False ) :
2020-11-02 20:06:52 +01:00
"""
Compiles the JAPL runtime , generating the appropriate
configuration needed for compilation to succeed .
Nim 1.2 or above is required to build JAPL
2020-11-02 21:37:57 +01:00
: param path : The path to JAPL ' s main source directory
2020-11-02 20:06:52 +01:00
: type path : string , optional
: param flags : Any extra compiler flags that will
be passed to nim . Keys and values should be strings ,
but any object with a proper __str__ or __repr__
method is probably fine as long as it ' s a valid
nim option for the compiler . Defaults to { } ( no flags )
: type flags : dict , optional
: param options : Compile - time options such as debugging the
compiler or the VM , defaults to { } ( use defaults )
: type options : dict , optional
"""
2020-11-02 21:37:57 +01:00
config_path = os . path . join ( path , " config.nim " )
main_path = os . path . join ( path , " japl.nim " )
2020-11-02 20:06:52 +01:00
logging . info ( " Just Another Build Tool, version 0.2 " )
if not os . path . exists ( path ) :
logging . error ( f " Input path ' { path } ' does not exist " )
return
2020-11-02 21:37:57 +01:00
if os . path . isfile ( config_path ) and not override :
logging . warning ( f " A config file exists at ' { config_path } ' , keeping it " )
2020-11-02 20:06:52 +01:00
else :
2020-12-26 17:01:03 +01:00
logging . warning ( f " Overriding config file at ' { config_path } ' " )
2020-11-02 21:37:57 +01:00
logging . debug ( f " Generating config file at ' { config_path } ' " )
try :
with open ( config_path , " w " ) as build_config :
build_config . write ( CONFIG_TEMPLATE . format (
* * options
) )
except Exception as fatal :
logging . error ( f " A fatal unhandled exception occurred -> { type ( fatal ) . __name__ } : { fatal } " )
else :
logging . debug ( f " Config file has been generated, compiling with options as follows: \n { pformat ( options , indent = 2 ) } " )
logging . debug ( f " Compiling ' { main_path } ' " )
2020-11-02 20:06:52 +01:00
nim_flags = " " . join ( f " - { name } : { value } " if len ( name ) == 1 else f " -- { name } : { value } " for name , value in flags . items ( ) )
command = " nim {flags} compile {path} "
2020-11-02 21:37:57 +01:00
command = command . format ( flags = nim_flags , path = main_path )
2020-11-02 20:06:52 +01:00
logging . debug ( f " Running ' { command } ' " )
logging . info ( " Compiling JAPL " )
start = time ( )
try :
2020-11-02 21:37:57 +01:00
process = Popen ( shlex . split ( command , posix = os . name != " nt " ) , stdout = DEVNULL , stderr = PIPE )
2020-11-02 20:06:52 +01:00
_ , stderr = process . communicate ( )
stderr = stderr . decode ( )
assert process . returncode == 0 , f " Command ' { command } ' exited with non-0 exit code { process . returncode } , output below: \n { stderr } "
except Exception as fatal :
logging . error ( f " A fatal unhandled exception occurred -> { type ( fatal ) . __name__ } : { fatal } " )
else :
logging . debug ( f " Compilation completed in { time ( ) - start : .2f } seconds " )
logging . info ( " Build completed " )
if __name__ == " __main__ " :
parser = argparse . ArgumentParser ( )
2020-11-02 21:37:57 +01:00
parser . add_argument ( " path " , help = " The path to JAPL ' s source directory " )
2020-11-02 20:06:52 +01:00
parser . add_argument ( " --verbose " , help = " Prints debug information to stdout " , action = " store_true " )
parser . add_argument ( " --flags " , help = " Optional flags to be passed to the nim compiler. Must be a comma-separated list of name:value (without spaces) " )
2020-11-02 21:37:57 +01:00
parser . add_argument ( " --options " , help = " Set compile-time options and constants, pass a comma-separated list of name:value (without spaces). "
" Note that if a config.nim file exists in the destination directory, that will override any setting defined here unless --override-config is used " )
parser . add_argument ( " --override-config " , help = " Overrides the setting of an already existing config.nim file in the destination directory " , action = " store_true " )
2020-11-02 20:06:52 +01:00
args = parser . parse_args ( )
2020-12-28 10:09:52 +01:00
flags = {
" gc " : " markAndSweep " ,
}
2020-11-02 20:06:52 +01:00
options = {
" debug_vm " : " false " ,
" debug_gc " : " false " ,
" debug_compiler " : " false " ,
" debug_alloc " : " false " ,
" map_load_factor " : " 0.75 " ,
" array_grow_factor " : " 2 " ,
" frames_max " : " 800 "
}
2020-11-04 03:46:47 +01:00
level = logging . DEBUG if args . verbose else logging . INFO
2020-11-02 20:06:52 +01:00
logging . basicConfig ( format = " [ %(levelname)s - %(asctime)s ] %(message)s " ,
datefmt = " % T " ,
level = level
)
if args . flags :
try :
for value in args . flags . split ( " , " ) :
k , v = value . split ( " : " , maxsplit = 2 )
flags [ k ] = v
except Exception :
logging . error ( " Invalid parameter for --flags " )
exit ( )
if args . options :
try :
for value in args . options . split ( " , " ) :
k , v = value . split ( " : " , maxsplit = 2 )
if k not in options :
logging . error ( " Invalid compile-time option " )
exit ( )
options [ k ] = v
except Exception :
logging . error ( " Invalid parameter for --options " )
exit ( )
2020-11-02 21:37:57 +01:00
build ( args . path , flags , options , args . override_config )
2020-11-02 20:06:52 +01:00
logging . debug ( " Build tool exited " )