2012-12-30 01:10:52 +08:00
#!/usr/bin/python3 -tt
# Copyright 2012 Jussi Pakkanen
# 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.
2013-01-14 07:13:55 +08:00
import os , stat , re
import interpreter , nodes
2012-12-30 01:10:52 +08:00
2013-01-02 05:43:25 +08:00
def shell_quote ( cmdlist ) :
return [ " ' " + x + " ' " for x in cmdlist ]
2013-01-14 07:13:55 +08:00
def do_conf_file ( src , dst , variables ) :
data = open ( src ) . readlines ( )
regex = re . compile ( ' @(.*?)@ ' )
result = [ ]
for line in data :
match = re . search ( regex , line )
while match :
varname = match . group ( 1 )
if varname in variables :
var = variables [ varname ]
if isinstance ( var , str ) :
pass
elif isinstance ( var , nodes . StringStatement ) :
var = var . get_string ( )
else :
raise RuntimeError ( ' Tried to replace a variable with something other than a string. ' )
else :
var = ' '
line = line . replace ( ' @ ' + varname + ' @ ' , var )
match = re . search ( regex , line )
result . append ( line )
open ( dst , ' w ' ) . writelines ( result )
2012-12-30 01:10:52 +08:00
class ShellGenerator ( ) :
2013-01-14 07:13:55 +08:00
def __init__ ( self , build , interp ) :
2013-01-12 04:59:49 +08:00
self . build = build
self . environment = build . environment
2013-01-14 07:13:55 +08:00
self . interpreter = interp
2012-12-30 01:10:52 +08:00
self . build_filename = ' compile.sh '
2013-01-07 01:13:30 +08:00
self . test_filename = ' run_tests.sh '
2013-01-12 08:25:06 +08:00
self . install_filename = ' install.sh '
2013-01-06 08:59:54 +08:00
self . processed_targets = { }
2012-12-30 01:10:52 +08:00
def generate ( self ) :
2013-01-07 01:13:30 +08:00
self . generate_compile_script ( )
self . generate_test_script ( )
2013-01-12 08:25:06 +08:00
self . generate_install_script ( )
2013-01-07 01:13:30 +08:00
2013-01-12 08:25:06 +08:00
def create_shfile ( self , outfilename , message ) :
2012-12-30 01:10:52 +08:00
outfile = open ( outfilename , ' w ' )
2012-12-30 04:13:14 +08:00
outfile . write ( ' #!/bin/sh \n \n ' )
2013-01-12 08:25:06 +08:00
outfile . write ( message )
2013-01-02 05:49:37 +08:00
cdcmd = [ ' cd ' , self . environment . get_build_dir ( ) ]
outfile . write ( ' ' . join ( shell_quote ( cdcmd ) ) + ' \n ' )
2012-12-30 01:10:52 +08:00
os . chmod ( outfilename , stat . S_IREAD | stat . S_IWRITE | stat . S_IEXEC | \
stat . S_IRGRP | stat . S_IXGRP | stat . S_IROTH | stat . S_IXOTH )
2013-01-12 08:25:06 +08:00
return outfile
def generate_compile_script ( self ) :
outfilename = os . path . join ( self . environment . get_build_dir ( ) , self . build_filename )
message = """ echo This is an autogenerated shell script build file for project \\ " %s \\ "
echo This is experimental and most likely will not work !
""" % s elf.build.get_project()
outfile = self . create_shfile ( outfilename , message )
self . generate_commands ( outfile )
outfile . close ( )
2012-12-30 01:10:52 +08:00
2013-01-07 01:13:30 +08:00
def generate_test_script ( self ) :
outfilename = os . path . join ( self . environment . get_build_dir ( ) , self . test_filename )
2013-01-12 08:25:06 +08:00
message = """ echo This is an autogenerated test script for project \\ " %s \\ "
echo This is experimental and most likely will not work !
echo Run compile . sh before this or bad things will happen .
""" % s elf.build.get_project()
outfile = self . create_shfile ( outfilename , message )
2013-01-07 01:13:30 +08:00
self . generate_tests ( outfile )
outfile . close ( )
2013-01-12 08:25:06 +08:00
def generate_install_script ( self ) :
outfilename = os . path . join ( self . environment . get_build_dir ( ) , self . install_filename )
message = """ echo This is an autogenerated install script for project \\ " %s \\ "
echo This is experimental and most likely will not work !
echo Run compile . sh before this or bad things will happen .
""" % s elf.build.get_project()
outfile = self . create_shfile ( outfilename , message )
2013-01-14 07:13:55 +08:00
self . generate_configure_files ( )
2013-01-12 19:53:19 +08:00
self . generate_target_install ( outfile )
self . generate_header_install ( outfile )
2013-01-12 20:31:43 +08:00
self . generate_man_install ( outfile )
2013-01-14 01:25:54 +08:00
self . generate_data_install ( outfile )
2013-01-12 08:25:06 +08:00
outfile . close ( )
2013-01-12 19:53:19 +08:00
def make_subdir ( self , outfile , dir ) :
cmdlist = [ ' mkdir ' , ' -p ' , dir ]
2013-01-12 20:31:43 +08:00
outfile . write ( ' ' . join ( shell_quote ( cmdlist ) ) + ' || exit \n ' )
2013-01-12 19:53:19 +08:00
def copy_file ( self , outfile , filename , outdir ) :
cpcommand = [ ' cp ' , filename , outdir ]
2013-01-12 20:31:43 +08:00
cpcommand = ' ' . join ( shell_quote ( cpcommand ) ) + ' || exit \n '
2013-01-12 19:53:19 +08:00
outfile . write ( cpcommand )
2013-01-14 07:13:55 +08:00
def generate_configure_files ( self ) :
for cf in self . build . get_configure_files ( ) :
infile = os . path . join ( self . environment . get_source_dir ( ) ,
cf . get_subdir ( ) ,
cf . get_source_name ( ) )
# FIXME, put in in the proper path.
outfile = os . path . join ( self . environment . get_build_dir ( ) ,
cf . get_target_name ( ) )
do_conf_file ( infile , outfile , self . interpreter . get_variables ( ) )
2013-01-14 01:25:54 +08:00
def generate_data_install ( self , outfile ) :
prefix = self . environment . get_prefix ( )
dataroot = os . path . join ( prefix , self . environment . get_datadir ( ) )
data = self . build . get_data ( )
if len ( data ) != 0 :
outfile . write ( ' \n echo Installing data files. \n ' )
else :
outfile . write ( ' \n echo This project has no data files to install. \n ' )
for d in data :
2013-01-14 02:50:16 +08:00
subdir = os . path . join ( dataroot , d . get_subdir ( ) )
2013-01-14 01:25:54 +08:00
absdir = os . path . join ( self . environment . get_prefix ( ) , subdir )
for f in d . get_sources ( ) :
self . make_subdir ( outfile , absdir )
srcabs = os . path . join ( self . environment . get_source_dir ( ) , f )
dstabs = os . path . join ( absdir , f )
self . copy_file ( outfile , srcabs , dstabs )
2013-01-12 20:31:43 +08:00
def generate_man_install ( self , outfile ) :
prefix = self . environment . get_prefix ( )
manroot = os . path . join ( prefix , self . environment . get_mandir ( ) )
man = self . build . get_man ( )
if len ( man ) != 0 :
outfile . write ( ' \n echo Installing man pages. \n ' )
else :
outfile . write ( ' \n echo This project has no man pages to install. \n ' )
for m in man :
for f in m . get_sources ( ) :
num = f . split ( ' . ' ) [ - 1 ]
subdir = ' man ' + num
absdir = os . path . join ( manroot , subdir )
self . make_subdir ( outfile , absdir )
srcabs = os . path . join ( self . environment . get_source_dir ( ) , f )
dstabs = os . path . join ( manroot ,
os . path . join ( subdir , f + ' .gz ' ) )
cmd = " gzip < ' %s ' > ' %s ' || exit \n " % ( srcabs , dstabs )
outfile . write ( cmd )
2013-01-12 19:53:19 +08:00
def generate_header_install ( self , outfile ) :
prefix = self . environment . get_prefix ( )
incroot = os . path . join ( prefix , self . environment . get_includedir ( ) )
headers = self . build . get_headers ( )
if len ( headers ) != 0 :
outfile . write ( ' \n echo Installing headers. \n ' )
else :
2013-01-12 20:31:43 +08:00
outfile . write ( ' \n echo This project has no headers to install. \n ' )
2013-01-12 19:53:19 +08:00
for h in headers :
outdir = os . path . join ( incroot , h . get_subdir ( ) )
self . make_subdir ( outfile , outdir )
for f in h . get_sources ( ) :
2013-01-12 20:31:43 +08:00
abspath = os . path . join ( self . environment . get_source_dir ( ) , f ) # FIXME
2013-01-12 19:53:19 +08:00
self . copy_file ( outfile , abspath , outdir )
2013-01-12 08:25:06 +08:00
2013-01-12 19:53:19 +08:00
def generate_target_install ( self , outfile ) :
2013-01-12 08:25:06 +08:00
prefix = self . environment . get_prefix ( )
libdir = os . path . join ( prefix , self . environment . get_libdir ( ) )
bindir = os . path . join ( prefix , self . environment . get_bindir ( ) )
2013-01-12 19:53:19 +08:00
self . make_subdir ( outfile , libdir )
self . make_subdir ( outfile , bindir )
2013-01-14 01:25:54 +08:00
if len ( self . build . get_targets ( ) ) != 0 :
outfile . write ( ' \n echo Installing targets. \n ' )
else :
outfile . write ( ' \n echo This project has no targets to install. \n ' )
2013-01-12 08:25:06 +08:00
for tmp in self . build . get_targets ( ) . items ( ) :
( name , t ) = tmp
if t . should_install ( ) :
if isinstance ( t , interpreter . Executable ) :
outdir = bindir
else :
outdir = libdir
outfile . write ( ' echo Installing " %s " . \n ' % name )
2013-01-12 19:53:19 +08:00
self . copy_file ( outfile , self . get_target_filename ( t ) , outdir )
2013-01-07 01:13:30 +08:00
def generate_tests ( self , outfile ) :
2013-01-12 04:59:49 +08:00
for t in self . build . get_tests ( ) :
2013-01-07 01:13:30 +08:00
cmds = [ ]
cmds . append ( self . get_target_filename ( t . get_exe ( ) ) )
outfile . write ( ' echo Running test \\ " %s \\ " . \n ' % t . get_name ( ) )
outfile . write ( ' ' . join ( shell_quote ( cmds ) ) + ' || exit \n ' )
2013-01-14 02:50:16 +08:00
def get_compiler_for_source ( self , src ) :
2013-01-12 04:59:49 +08:00
for i in self . build . compilers :
2012-12-30 01:51:32 +08:00
if i . can_compile ( src ) :
2013-01-14 02:50:16 +08:00
return i
raise RuntimeError ( ' No specified compiler can handle file ' + src )
def generate_basic_compiler_arguments ( self , target , compiler ) :
2012-12-30 04:04:24 +08:00
commands = [ ]
commands + = compiler . get_exelist ( )
2012-12-30 02:02:37 +08:00
commands + = compiler . get_debug_flags ( )
2012-12-30 01:51:32 +08:00
commands + = compiler . get_std_warn_flags ( )
commands + = compiler . get_compile_only_flags ( )
2013-01-06 03:08:08 +08:00
if isinstance ( target , interpreter . SharedLibrary ) :
commands + = compiler . get_pic_flags ( )
2012-12-30 06:55:35 +08:00
for dep in target . get_external_deps ( ) :
commands + = dep . get_compile_flags ( )
2013-01-14 02:50:16 +08:00
return commands
def get_pch_include_args ( self , compiler , target ) :
args = [ ]
pchpath = self . get_target_dir ( target )
includearg = compiler . get_include_arg ( pchpath )
for p in target . get_pch ( ) :
if compiler . can_compile ( p ) :
args . append ( ' -include ' )
args . append ( os . path . split ( p ) [ - 1 ] )
if len ( args ) > 0 :
args = [ includearg ] + args
return args
def generate_single_compile ( self , target , outfile , src ) :
compiler = self . get_compiler_for_source ( src )
commands = self . generate_basic_compiler_arguments ( target , compiler )
abs_src = os . path . join ( self . environment . get_source_dir ( ) , target . get_source_subdir ( ) , src )
abs_obj = os . path . join ( self . get_target_dir ( target ) , src )
abs_obj + = ' . ' + self . environment . get_object_suffix ( )
commands + = self . get_pch_include_args ( compiler , target )
2012-12-30 01:51:32 +08:00
commands . append ( abs_src )
commands + = compiler . get_output_flags ( )
commands . append ( abs_obj )
2013-01-02 05:43:25 +08:00
quoted = shell_quote ( commands )
2012-12-30 04:13:14 +08:00
outfile . write ( ' \n echo Compiling \\ " %s \\ " \n ' % src )
2012-12-30 07:08:50 +08:00
outfile . write ( ' ' . join ( quoted ) + ' || exit \n ' )
2012-12-30 01:51:32 +08:00
return abs_obj
2013-01-07 00:40:32 +08:00
2013-01-06 08:59:54 +08:00
def build_target_link_arguments ( self , deps ) :
args = [ ]
for d in deps :
2013-01-07 00:40:32 +08:00
if not isinstance ( d , interpreter . StaticLibrary ) and \
not isinstance ( d , interpreter . SharedLibrary ) :
raise RuntimeError ( ' Tried to link with a non-library target " %s " . ' % d . get_basename ( ) )
2013-01-06 08:59:54 +08:00
args . append ( self . get_target_filename ( d ) )
return args
2012-12-30 01:51:32 +08:00
2012-12-30 06:55:35 +08:00
def generate_link ( self , target , outfile , outname , obj_list ) :
2013-01-06 00:13:38 +08:00
if isinstance ( target , interpreter . StaticLibrary ) :
2013-01-12 04:59:49 +08:00
linker = self . build . static_linker
2013-01-06 00:13:38 +08:00
else :
2013-01-12 04:59:49 +08:00
linker = self . build . compilers [ 0 ] # Fixme.
2012-12-30 04:04:24 +08:00
commands = [ ]
commands + = linker . get_exelist ( )
2013-01-06 03:08:08 +08:00
if isinstance ( target , interpreter . Executable ) :
commands + = linker . get_std_exe_link_flags ( )
elif isinstance ( target , interpreter . SharedLibrary ) :
commands + = linker . get_std_shared_lib_link_flags ( )
2013-01-06 03:12:14 +08:00
commands + = linker . get_pic_flags ( )
2013-01-06 03:08:08 +08:00
elif isinstance ( target , interpreter . StaticLibrary ) :
commands + = linker . get_std_link_flags ( )
else :
raise RuntimeError ( ' Unknown build target type. ' )
2012-12-30 06:55:35 +08:00
for dep in target . get_external_deps ( ) :
commands + = dep . get_link_flags ( )
2012-12-30 04:04:24 +08:00
commands + = linker . get_output_flags ( )
commands . append ( outname )
2013-01-06 00:13:38 +08:00
commands + = obj_list
2013-01-06 08:59:54 +08:00
commands + = self . build_target_link_arguments ( target . get_dependencies ( ) )
2013-01-02 05:43:25 +08:00
quoted = shell_quote ( commands )
2013-01-02 06:56:46 +08:00
outfile . write ( ' \n echo Linking \\ " %s \\ " . \n ' % target . get_basename ( ) )
2012-12-30 07:08:50 +08:00
outfile . write ( ' ' . join ( quoted ) + ' || exit \n ' )
2012-12-30 01:51:32 +08:00
2013-01-02 06:00:24 +08:00
def get_target_dir ( self , target ) :
dirname = os . path . join ( self . environment . get_build_dir ( ) , target . get_basename ( ) )
os . makedirs ( dirname , exist_ok = True )
return dirname
2012-12-30 01:51:32 +08:00
def generate_commands ( self , outfile ) :
2013-01-12 04:59:49 +08:00
for i in self . build . get_targets ( ) . items ( ) :
2013-01-02 06:00:24 +08:00
target = i [ 1 ]
2013-01-06 08:59:54 +08:00
self . generate_target ( target , outfile )
def process_target_dependencies ( self , target , outfile ) :
for t in target . get_dependencies ( ) :
tname = t . get_basename ( )
if not tname in self . processed_targets :
self . generate_target ( t , outfile )
def get_target_filename ( self , target ) :
targetdir = self . get_target_dir ( target )
filename = os . path . join ( targetdir , target . get_filename ( ) )
return filename
2013-01-14 02:50:16 +08:00
def generate_pch ( self , target , outfile ) :
print ( ' Generating pch for " %s " ' % target . get_basename ( ) )
for pch in target . get_pch ( ) :
if ' / ' not in pch :
raise interpreter . InvalidArguments ( ' Precompiled header of " %s " must not be in the same direcotory as source, please put it in a subdirectory. ' % target . get_basename ( ) )
compiler = self . get_compiler_for_source ( pch )
commands = self . generate_basic_compiler_arguments ( target , compiler )
srcabs = os . path . join ( self . environment . get_source_dir ( ) , target . get_source_subdir ( ) , pch )
dstabs = os . path . join ( self . environment . get_build_dir ( ) ,
self . get_target_dir ( target ) ,
os . path . split ( pch ) [ - 1 ] + ' . ' + compiler . get_pch_suffix ( ) )
commands . append ( srcabs )
commands + = compiler . get_output_flags ( )
commands . append ( dstabs )
quoted = shell_quote ( commands )
outfile . write ( ' \n echo Generating pch \\ " %s \\ " . \n ' % pch )
outfile . write ( ' ' . join ( quoted ) + ' || exit \n ' )
2013-01-06 08:59:54 +08:00
def generate_target ( self , target , outfile ) :
name = target . get_basename ( )
if name in self . processed_targets :
return
self . process_target_dependencies ( target , outfile )
print ( ' Generating target ' , name )
outname = self . get_target_filename ( target )
obj_list = [ ]
2013-01-14 02:50:16 +08:00
if target . has_pch ( ) :
self . generate_pch ( target , outfile )
2013-01-06 08:59:54 +08:00
for src in target . get_sources ( ) :
obj_list . append ( self . generate_single_compile ( target , outfile , src ) )
self . generate_link ( target , outfile , outname , obj_list )
self . processed_targets [ name ] = True
2012-12-30 01:51:32 +08:00
2012-12-30 01:10:52 +08:00
if __name__ == ' __main__ ' :
code = """
project ( ' simple generator ' )
language ( ' c ' )
2013-01-01 23:47:58 +08:00
executable ( ' prog ' , ' prog.c ' , ' dep.c ' )
2012-12-30 01:10:52 +08:00
"""
2013-01-06 03:08:08 +08:00
import environment
2012-12-30 01:10:52 +08:00
os . chdir ( os . path . split ( __file__ ) [ 0 ] )
2013-01-01 23:47:58 +08:00
envir = environment . Environment ( ' . ' , ' work area ' )
2013-01-02 03:00:22 +08:00
intpr = interpreter . Interpreter ( code , envir )
2013-01-01 23:47:58 +08:00
g = ShellGenerator ( intpr , envir )
2012-12-30 01:10:52 +08:00
g . generate ( )