mudgangster

Tiny, scriptable MUD client
Log | Files | Refs | README

gen_ninja.lua (9531B)


      1 local lfs = require( "INTERNAL_LFS" )
      2 
      3 local configs = { }
      4 
      5 configs[ "windows" ] = {
      6 	bin_suffix = ".exe",
      7 	obj_suffix = ".obj",
      8 	lib_suffix = ".lib",
      9 
     10 	toolchain = "msvc",
     11 
     12 	cxxflags = "/c /Oi /Gm- /nologo /DNOMINMAX /DWIN32_LEAN_AND_MEAN",
     13 	ldflags = "user32.lib shell32.lib advapi32.lib dbghelp.lib /NOLOGO",
     14 }
     15 
     16 configs[ "windows-debug" ] = {
     17 	cxxflags = "/MTd /Z7",
     18 	ldflags = "/DEBUG",
     19 }
     20 configs[ "windows-release" ] = {
     21 	cxxflags = "/O2 /MT /DNDEBUG",
     22 	bin_prefix = "release/",
     23 }
     24 configs[ "windows-bench" ] = {
     25 	bin_suffix = "-bench.exe",
     26 	cxxflags = configs[ "windows-release" ].cxxflags,
     27 	ldflags = configs[ "windows-release" ].ldflags,
     28 	prebuilt_lib_dir = "windows-release",
     29 }
     30 
     31 configs[ "linux" ] = {
     32 	obj_suffix = ".o",
     33 	lib_prefix = "lib",
     34 	lib_suffix = ".a",
     35 
     36 	toolchain = "gcc",
     37 	cc = "gcc",
     38 	cxx = "g++",
     39 
     40 	cxxflags = "-c -fdiagnostics-color",
     41 	ldflags = "-fuse-ld=gold",
     42 }
     43 
     44 configs[ "linux-debug" ] = {
     45 	cxxflags = "-O0 -ggdb3 -fno-omit-frame-pointer",
     46 }
     47 configs[ "linux-asan" ] = {
     48 	bin_suffix = "-asan",
     49 	cxxflags = configs[ "linux-debug" ].cxxflags .. " -fsanitize=address",
     50 	ldflags = "-fsanitize=address",
     51 	prebuilt_lib_dir = "linux-debug",
     52 }
     53 configs[ "linux-release" ] = {
     54 	cxxflags = "-O2 -DNDEBUG",
     55 	ldflags = "-s",
     56 	bin_prefix = "release/",
     57 }
     58 configs[ "linux-bench" ] = {
     59 	bin_suffix = "-bench",
     60 	cxxflags = configs[ "linux-release" ].cxxflags,
     61 	ldflags = configs[ "linux-release" ].ldflags,
     62 	prebuilt_lib_dir = "linux-release",
     63 }
     64 
     65 local function identify_host()
     66 	local dll_ext = package.cpath:match( "(%a+)$" )
     67 
     68 	if dll_ext == "dll" then
     69 		return "windows"
     70 	end
     71 
     72 	local p = assert( io.popen( "uname -s" ) )
     73 	local uname = assert( p:read( "*all" ) ):gsub( "%s*$", "" )
     74 	assert( p:close() )
     75 
     76 	if uname == "Linux" then
     77 		return "linux"
     78 	end
     79 
     80 	io.stderr:write( "can't identify host OS" )
     81 	os.exit( 1 )
     82 end
     83 
     84 OS = identify_host()
     85 config = arg[ 1 ] or "debug"
     86 
     87 local OS_config = OS .. "-" .. config
     88 
     89 if not configs[ OS_config ] then
     90 	io.stderr:write( "bad config: " .. OS_config .. "\n" )
     91 	os.exit( 1 )
     92 end
     93 
     94 local function concat( key )
     95 	return ""
     96 		.. ( ( configs[ OS ] and configs[ OS ][ key ] ) or "" )
     97 		.. " "
     98 		.. ( ( configs[ OS_config ] and configs[ OS_config ][ key ] ) or "" )
     99 end
    100 
    101 local function rightmost( key )
    102 	return nil
    103 		or ( configs[ OS_config ] and configs[ OS_config ][ key ] )
    104 		or ( configs[ OS ] and configs[ OS ][ key ] )
    105 		or ""
    106 end
    107 
    108 local bin_prefix = rightmost( "bin_prefix" )
    109 local bin_suffix = rightmost( "bin_suffix" )
    110 local obj_suffix = rightmost( "obj_suffix" )
    111 local lib_prefix = rightmost( "lib_prefix" )
    112 local lib_suffix = rightmost( "lib_suffix" )
    113 local prebuilt_lib_dir = rightmost( "prebuilt_lib_dir" )
    114 prebuilt_lib_dir = prebuilt_lib_dir == "" and OS_config or prebuilt_lib_dir
    115 local cxxflags = concat( "cxxflags" )
    116 local ldflags = concat( "ldflags" )
    117 
    118 toolchain = rightmost( "toolchain" )
    119 
    120 local dir = "build/" .. OS_config
    121 local output = { }
    122 
    123 local function flatten_into( res, t )
    124 	if type( t ) == "table" then
    125 		for _, x in ipairs( t ) do
    126 			flatten_into( res, x )
    127 		end
    128 	else
    129 		table.insert( res, t )
    130 	end
    131 end
    132 
    133 local function flatten( t )
    134 	local res = { }
    135 	flatten_into( res, t )
    136 	return res
    137 end
    138 
    139 local function join( names, suffix, prefix )
    140 	if not names then
    141 		return ""
    142 	end
    143 
    144 	prefix = prefix or ""
    145 	local flat = flatten( names )
    146 	for i = 1, #flat do
    147 		flat[ i ] = dir .. "/" .. prefix .. flat[ i ] .. suffix
    148 	end
    149 	return table.concat( flat, " " )
    150 end
    151 
    152 local function joinpb( names, suffix, prefix )
    153 	if not names then
    154 		return ""
    155 	end
    156 
    157 	prefix = prefix or ""
    158 	local flat = flatten( names )
    159 	for i = 1, #flat do
    160 		flat[ i ] = "libs/" .. flat[ i ] .. "/" .. prebuilt_lib_dir .. "/" .. prefix .. flat[ i ] .. suffix
    161 	end
    162 	return table.concat( flat, " " )
    163 end
    164 
    165 function printf( form, ... )
    166 	print( form:format( ... ) )
    167 end
    168 
    169 local objs = { }
    170 local objs_flags = { }
    171 local objs_extra_flags = { }
    172 
    173 local bins = { }
    174 local bins_flags = { }
    175 local bins_extra_flags = { }
    176 
    177 local libs = { }
    178 
    179 local function glob_impl( dir, rel, res, prefix, suffix, recursive )
    180 	for filename in lfs.dir( dir .. rel ) do
    181 		if filename ~= "." and filename ~= ".." then
    182 			local fullpath = dir .. rel .. "/" .. filename
    183 			local attr = lfs.attributes( fullpath )
    184 
    185 			if attr.mode == "directory" then
    186 				if recursive then
    187 					glob_impl( dir, rel .. "/" .. filename, res, prefix, suffix, true )
    188 				end
    189 			else
    190 				local prefix_start = dir:len() + rel:len() + 2
    191 				if fullpath:find( prefix, prefix_start, true ) == prefix_start and fullpath:sub( -suffix:len() ) == suffix then
    192 					table.insert( res, fullpath )
    193 				end
    194 			end
    195 		end
    196 	end
    197 end
    198 
    199 function glob( srcs )
    200 	local res = { }
    201 	for _, pattern in ipairs( flatten( srcs ) ) do
    202 		if pattern:find( "*", 1, true ) then
    203 			local dir, prefix, suffix = pattern:match( "^(.-)/?([^/*]*)%*+(.*)$" )
    204 			local recursive = pattern:find( "**", 1, true ) ~= nil
    205 			assert( not recursive or prefix == "" )
    206 
    207 			glob_impl( dir, "", res, prefix, suffix, recursive )
    208 		else
    209 			table.insert( res, pattern )
    210 		end
    211 	end
    212 	return res
    213 end
    214 
    215 local function add_srcs( srcs )
    216 	for _, src in ipairs( srcs ) do
    217 		if not objs[ src ] then
    218 			objs[ src ] = { }
    219 		end
    220 	end
    221 end
    222 
    223 function bin( bin_name, cfg )
    224 	assert( type( cfg ) == "table", "cfg should be a table" )
    225 	assert( type( cfg.srcs ) == "table", "cfg.srcs should be a table" )
    226 	assert( not cfg.libs or type( cfg.libs ) == "table", "cfg.libs should be a table or nil" )
    227 	assert( not cfg.prebuilt_libs or type( cfg.prebuilt_libs ) == "table", "cfg.prebuilt_libs should be a table or nil" )
    228 	assert( not bins[ bin_name ] )
    229 
    230 	bins[ bin_name ] = cfg
    231 	cfg.srcs = glob( cfg.srcs )
    232 	add_srcs( cfg.srcs )
    233 end
    234 
    235 function lib( lib_name, srcs )
    236 	assert( type( srcs ) == "table", "srcs should be a table" )
    237 	assert( not libs[ lib_name ] )
    238 
    239 	local globbed = glob( srcs )
    240 	libs[ lib_name ] = globbed
    241 	add_srcs( globbed )
    242 end
    243 
    244 function obj_cxxflags( pattern, flags )
    245 	table.insert( objs_extra_flags, { pattern = pattern, flags = flags } )
    246 end
    247 
    248 function obj_dependencies( src, dependencies )
    249 	objs[ src ].dependencies = dependencies
    250 end
    251 
    252 function obj_replace_cxxflags( pattern, flags )
    253 	table.insert( objs_flags, { pattern = pattern, flags = flags } )
    254 end
    255 
    256 local function toolchain_helper( t, f )
    257 	return function( ... )
    258 		if toolchain == t then
    259 			f( ... )
    260 		end
    261 	end
    262 end
    263 
    264 msvc_obj_cxxflags = toolchain_helper( "msvc", obj_cxxflags )
    265 msvc_obj_replace_cxxflags = toolchain_helper( "msvc", obj_replace_cxxflags )
    266 
    267 gcc_obj_cxxflags = toolchain_helper( "gcc", obj_cxxflags )
    268 gcc_obj_replace_cxxflags = toolchain_helper( "gcc", obj_replace_cxxflags )
    269 
    270 printf( "builddir = build" )
    271 printf( "cxxflags = %s", cxxflags )
    272 printf( "ldflags = %s", ldflags )
    273 
    274 if toolchain == "msvc" then
    275 
    276 printf( "lua = ggbuild/lua.exe" )
    277 
    278 printf( [[
    279 rule cpp
    280     command = cl /showIncludes $cxxflags $extra_cxxflags -Fo$out $in
    281     description = $in
    282     deps = msvc
    283 
    284 rule bin
    285     command = link /OUT:$out $in $ldflags $extra_ldflags
    286     description = $out
    287 
    288 rule lib
    289     command = lib /NOLOGO /OUT:$out $in
    290     description = $out
    291 
    292 rule rc
    293     command = rc /fo$out /nologo $in_rc
    294     description = $in
    295 ]] )
    296 
    297 elseif toolchain == "gcc" then
    298 
    299 printf( "cpp = %s", rightmost( "cxx" ) )
    300 printf( "lua = ggbuild/lua.linux" )
    301 printf( [[
    302 rule cpp
    303     command = $cpp -MD -MF $out.d $cxxflags $extra_cxxflags -c -o $out $in
    304     depfile = $out.d
    305     description = $in
    306     deps = gcc
    307 
    308 rule bin
    309     command = $cpp -o $out $in $ldflags $extra_ldflags
    310     description = $out
    311 
    312 rule lib
    313     command = ar rs $out $in
    314     description = $out
    315 ]] )
    316 
    317 end
    318 
    319 local function rule_for_src( src_name )
    320 	local ext = src_name:match( "([^%.]+)$" )
    321 	return ( { cc = "cpp", cpp = "cpp" } )[ ext ]
    322 end
    323 
    324 local function write_ninja_script()
    325 	for _, flag in ipairs( objs_flags ) do
    326 		for name, cfg in pairs( objs ) do
    327 			if name:match( flag.pattern ) then
    328 				cfg.cxxflags = flag.flags
    329 			end
    330 		end
    331 	end
    332 
    333 	for _, flag in ipairs( objs_extra_flags ) do
    334 		for name, cfg in pairs( objs ) do
    335 			if name:match( flag.pattern ) then
    336 				cfg.extra_cxxflags = ( cfg.extra_cxxflags or "" ) .. " " .. flag.flags
    337 			end
    338 		end
    339 	end
    340 
    341 	for src_name, cfg in pairs( objs ) do
    342 		local rule = rule_for_src( src_name )
    343 		local deps = cfg.dependencies and ( " | " .. cfg.dependencies ) or ""
    344 		printf( "build %s/%s%s: %s %s%s", dir, src_name, obj_suffix, rule, src_name, deps )
    345 		if cfg.cxxflags then
    346 			printf( "    cxxflags = %s", cfg.cxxflags )
    347 		end
    348 		if cfg.extra_cxxflags then
    349 			printf( "    extra_cxxflags = %s", cfg.extra_cxxflags )
    350 		end
    351 	end
    352 
    353 	for lib_name, srcs in pairs( libs ) do
    354 		printf( "build %s/%s%s%s: lib %s", dir, lib_prefix, lib_name, lib_suffix, join( srcs, obj_suffix ) )
    355 	end
    356 
    357 	for bin_name, cfg in pairs( bins ) do
    358 		local srcs = { cfg.srcs }
    359 
    360 		if OS == "windows" and cfg.rc then
    361 			srcs = { cfg.srcs, cfg.rc }
    362 			printf( "build %s/%s%s: rc %s.rc", dir, cfg.rc, obj_suffix, cfg.rc )
    363 			printf( "    in_rc = %s.rc", cfg.rc )
    364 		end
    365 
    366 		local full_name = bin_prefix .. bin_name .. bin_suffix
    367 		printf( "build %s: bin %s %s %s",
    368 			full_name,
    369 			join( srcs, obj_suffix ),
    370 			join( cfg.libs, lib_suffix, lib_prefix ),
    371 			joinpb( cfg.prebuilt_libs, lib_suffix, lib_prefix )
    372 		)
    373 
    374 		local ldflags_key = toolchain .. "_ldflags"
    375 		local extra_ldflags_key = toolchain .. "_extra_ldflags"
    376 		if cfg[ ldflags_key ] then
    377 			printf( "    ldflags = %s", cfg[ ldflags_key ] )
    378 		end
    379 		if cfg[ extra_ldflags_key ] then
    380 			printf( "    extra_ldflags = %s", cfg[ extra_ldflags_key ] )
    381 		end
    382 
    383 		printf( "default %s", full_name )
    384 	end
    385 end
    386 
    387 automatically_print_output_at_exit = setmetatable( { }, { __gc = write_ninja_script } )