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 } )