├── .gitignore ├── .gitmodules ├── README.md ├── libs └── omf │ └── msvcrt.lib ├── local.ini.sample ├── samples ├── .gitignore ├── 01-msgbox │ ├── hello.d │ └── slim.ini ├── 02-console │ ├── hello.d │ └── slim.ini ├── 03-modules │ ├── a.d │ ├── b.d │ └── slim.ini ├── 04-phobos │ ├── slim.ini │ └── sum.d ├── 05-dll │ ├── dll.d │ ├── dllfuns.d │ └── slim.ini └── 06-dlluser │ ├── dlluser.d │ └── slim.ini └── slimbuild └── slimbuild.d /.gitignore: -------------------------------------------------------------------------------- 1 | *.exe 2 | *.dll 3 | *.obj 4 | *.map 5 | *.json 6 | *.deps 7 | *.log 8 | *.ilk 9 | *.pdb 10 | 11 | local.ini 12 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "slimbuild/ae"] 2 | path = slimbuild/ae 3 | url = https://github.com/CyberShadow/ae 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | SlimD 2 | ===== 3 | 4 | Bare-bones D on Win32. 5 | 6 | [Announcement](http://forum.dlang.org/post/qcbicxrtmjmwiljsyhdf@forum.dlang.org). 7 | 8 | Setup 9 | ----- 10 | 11 | 1. Clone the repository: 12 | 13 | git clone --recursive https://github.com/CyberShadow/SlimD 14 | 15 | 2. Build the build tool: 16 | 17 | cd SlimD 18 | rdmd --build-only slimbuild\slimbuild 19 | 20 | 3. Add `slimbuild` to the system `PATH` (optional); 21 | 22 | 4. Copy `local.ini.sample` to `local.ini` and edit according to the comments (optional). 23 | 24 | Usage 25 | ----- 26 | 27 | See `samples` for examples. Run `slimbuild` from within a sample's directory to build it. 28 | 29 | The build tool will use configuration from INI files and the command line. 30 | Configuration is applied in the following order: 31 | 32 | 1. `local.ini` in `slimbuild`'s parent directory (the repository root); 33 | 2. `slim.ini` in the current directory; 34 | 3. `local.ini` in the current directory; 35 | 4. The command line (`--option=value`). 36 | -------------------------------------------------------------------------------- /libs/omf/msvcrt.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CyberShadow/SlimD/bfef89c43c4b44f28780c243dc68bf5d99a223c0/libs/omf/msvcrt.lib -------------------------------------------------------------------------------- /local.ini.sample: -------------------------------------------------------------------------------- 1 | ; Machine-local SlimD configuration. 2 | ; You can specify project default configuration, 3 | ; or tool paths here. 4 | 5 | ; Default compiler. 6 | ;compiler=dmd 7 | 8 | ; Default linker. 9 | ;linker=optlink 10 | 11 | ; Default build flags. 12 | ;dflags=-betterC -release 13 | 14 | ; Library search paths, e.g. if you want to link against Phobos. 15 | ;libPath.omf.phobos = C:\dmd2\windows\lib 16 | ;libPath.coff32.phobos = C:\dmd2\windows\lib32mscoff 17 | ;libPath.coff64.phobos = C:\dmd2\windows\lib64 18 | 19 | ; You can specify full paths to tools here, 20 | ; in case they are not in your system PATH. 21 | 22 | ; DigitalMars D compiler 23 | [tools.dmd] 24 | ;path=C:\dmd2\windows\bin\ 25 | ;command=dmd 26 | 27 | ; LLVM D compiler 28 | [tools.ldc] 29 | ;path=C:\ldc2\bin;C:\mingw32-dw2\bin;C:\mingw32-dw2\i686-w64-mingw32\bin;C:\mingw32-dw2\libexec\gcc\i686-w64-mingw32\4.8.0 30 | ;command=ldc2 31 | 32 | ; UniLink 33 | [tools.unilink] 34 | ;path=C:\UniLink\ 35 | ;command=ulink 36 | 37 | ; Microsoft Linker 38 | [tools.mslink] 39 | ;path=C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\bin\ 40 | ;command=link 41 | 42 | ; Crinkler 43 | [tools.crinkler] 44 | ;path=C:\Crinkler\ 45 | ;command=crinkler 46 | 47 | ; GoLink (not to be confused with Google's programming language) 48 | [tools.golink] 49 | ;path=C:\GoDev\ 50 | ;command=GoLink 51 | 52 | ; Open Watcom 53 | [tools.watcom] 54 | ;path=C:\WATCOM\ 55 | ;command=wlink 56 | 57 | ; Gnu Compiler Collection 58 | [tools.gcc] 59 | ;path=C:\Soft\mingw32-dw2\bin\ 60 | ;command=gcc 61 | 62 | ; D demangler, for the --demangle option 63 | [tools.ddemangle] 64 | ;command=ddemangle 65 | -------------------------------------------------------------------------------- /samples/.gitignore: -------------------------------------------------------------------------------- 1 | *.lib 2 | -------------------------------------------------------------------------------- /samples/01-msgbox/hello.d: -------------------------------------------------------------------------------- 1 | // This example doesn't use the runtime at all, since 2 | // it specifies the entry point and doesn't use any 3 | // language facilities that require runtime support. 4 | // Therefore, it doesn't even require slimlib. 5 | 6 | // The makefile has two targets: optlink (default) and 7 | // unilink. UniLink will produce a much smaller executable. 8 | 9 | // The compiler will emit some meta-information for 10 | // every module. It's only a few bytes in size, though. 11 | module hello; 12 | 13 | // Disable ModuleInfo generation for LDC. 14 | version(LDC) pragma(LDC_no_moduleinfo); 15 | 16 | // We only need this for the function signatures. 17 | import core.sys.windows.windows; 18 | 19 | // No magic names needed. 20 | extern(C) 21 | void start() 22 | { 23 | MessageBox(null, "Hello, world!", "SlimD", MB_ICONINFORMATION); 24 | 25 | // Omitting ExitProcess may work on some systems, 26 | // but will cause a crash on exit on others. 27 | // ExitProcess(0); 28 | } 29 | 30 | // pragma(startaddress) sets the executable's real entry point, 31 | // as it will end up in the PE header. We do this to bypass 32 | // runtime initialization and all its dependencies. 33 | // Unfortunately DMD does not emit the correct information to 34 | // COFF object files: 35 | // https://issues.dlang.org/show_bug.cgi?id=13431 36 | // So for COFF linkers it is passed on the linker's command 37 | // line. The name can be overridden in the makefile. 38 | //pragma(startaddress, start); 39 | 40 | // If linking with DMD, DMD will automatically add kernel32 41 | // and user32 to the linker's command line. Otherwise, these 42 | // libraries need to be specified here, or in the makefile. 43 | // Some linkers don't understand lib pragmas, though, so 44 | // you'll need to list the libraries in the makefile instead. 45 | //pragma(lib, "kernel32"); 46 | //pragma(lib, "user32"); 47 | -------------------------------------------------------------------------------- /samples/01-msgbox/slim.ini: -------------------------------------------------------------------------------- 1 | ; SlimBuild configuration file. 2 | ; All settings can be specified either here (slim.ini), local.ini 3 | ; (unversioned overrides), or ../../local.ini (local settings). 4 | 5 | ; Project name. 6 | ; Indicates name of output file and default module list. 7 | name=hello 8 | 9 | ; Libraries we want to link to. 10 | libs=user32.lib kernel32.lib 11 | 12 | ; To target Win64, simply specify the model. 13 | ;model=64 14 | -------------------------------------------------------------------------------- /samples/02-console/hello.d: -------------------------------------------------------------------------------- 1 | module hello; 2 | 3 | import core.stdc.stdio; 4 | 5 | extern(C) 6 | void start() 7 | { 8 | puts("Hello, world!"); 9 | } 10 | 11 | pragma(startaddress, start); 12 | -------------------------------------------------------------------------------- /samples/02-console/slim.ini: -------------------------------------------------------------------------------- 1 | ; SlimBuild configuration file. 2 | 3 | name=hello 4 | 5 | ; Link against the standard msvcrt.dll present on all recent Windows versions. 6 | libs=msvcrt.lib 7 | 8 | ; Create an executable with the CONSOLE subsystem. 9 | console=true 10 | 11 | ; To target Win64, simply specify the model. 12 | ;model=64 13 | -------------------------------------------------------------------------------- /samples/03-modules/a.d: -------------------------------------------------------------------------------- 1 | module a; 2 | 3 | import core.sys.windows.windows; 4 | import b; 5 | 6 | extern(C) 7 | void start() 8 | { 9 | MessageBox(null, getMessage(), getTitle(), MB_ICONINFORMATION); 10 | } 11 | 12 | pragma(startaddress, start); 13 | -------------------------------------------------------------------------------- /samples/03-modules/b.d: -------------------------------------------------------------------------------- 1 | const(char)* getTitle() 2 | { 3 | return "SlimD"; 4 | } 5 | 6 | const(char)* getMessage() 7 | { 8 | return "Hello, world!"; 9 | } 10 | -------------------------------------------------------------------------------- /samples/03-modules/slim.ini: -------------------------------------------------------------------------------- 1 | name=modules 2 | libs=user32.lib kernel32.lib 3 | 4 | modules=a.d b.d 5 | -------------------------------------------------------------------------------- /samples/04-phobos/slim.ini: -------------------------------------------------------------------------------- 1 | name=sum 2 | libs=user32.lib kernel32.lib shell32.lib msvcrt.lib phobos.lib 3 | console=true 4 | -------------------------------------------------------------------------------- /samples/04-phobos/sum.d: -------------------------------------------------------------------------------- 1 | /// Calculates the sum of all integers from 1 to N, where 2 | /// N is given as the first argument on the command line. 3 | 4 | module sum; 5 | 6 | import core.stdc.stdio; 7 | import core.stdc.wchar_; 8 | import core.sys.windows.windows; 9 | 10 | // We can use Phobos functions, as long as they are @nogc 11 | // and nothrow. 12 | 13 | // Memory allocations invoke the GC, which pulls in a huge 14 | // part of the runtime, and requires initialization. 15 | // Runtime initialization would mean that we'd need to use 16 | // the D main() function as our entry point. It would also 17 | // pull in the C main and its dependencies from the C 18 | // runtime as well. 19 | 20 | // We can't use exceptions because they require memory 21 | // allocation, and thus cause all of the above problems as 22 | // well. 23 | 24 | import std.algorithm; 25 | import std.conv; 26 | import std.range; 27 | 28 | // Why is this not in Phobos already? 29 | inout(wchar)[] fromWStringz(inout(wchar)* p) 30 | { 31 | return p ? p[0..wcslen(p)] : null; 32 | } 33 | 34 | extern(C) 35 | void start() nothrow @nogc 36 | { 37 | // Get command line. There is no args[] or 38 | // argc/argv passed to us, we have to query it 39 | // (the runtime usually does this for you). 40 | int argc; 41 | auto argv = CommandLineToArgvW(GetCommandLineW(), &argc); 42 | 43 | // swscanf because std.conv.to/parse may throw 44 | int n; swscanf(argv[1], "%d", &n); 45 | 46 | // reduce with seed because seedless version throws on 47 | // an empty range 48 | auto sum = reduce!"a+b"(0, iota(n+1)); 49 | 50 | // printf, because writeln may throw/allocate 51 | printf("%d\n", sum); 52 | } 53 | 54 | pragma(startaddress, start); 55 | -------------------------------------------------------------------------------- /samples/05-dll/dll.d: -------------------------------------------------------------------------------- 1 | module dll; 2 | 3 | import core.sys.windows.windows; 4 | 5 | /// Like WinMain, DllMain is not the true entry point of DLLs. 6 | /// Rather, the C runtime calls DllMain after initializing itself. 7 | /// We can circumvent the C runtime dependency by declaring the 8 | /// entry point ourselves, which incidentally has the same 9 | /// signature as DllMain. 10 | extern(System) 11 | BOOL DllEntryPoint(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved) nothrow @nogc 12 | { 13 | return TRUE; 14 | } 15 | 16 | pragma(startaddress, DllEntryPoint); 17 | -------------------------------------------------------------------------------- /samples/05-dll/dllfuns.d: -------------------------------------------------------------------------------- 1 | /// Actual functions declared by this DLL. 2 | /// We place the exported functions in a separate 3 | /// module from the DLL entry point, so that this 4 | /// module can be imported by DLL users (see 5 | /// 06-dlluser). 6 | 7 | module dllfuns; 8 | 9 | import core.sys.windows.windows; 10 | 11 | export void fun() 12 | { 13 | MessageBoxA(null, "Hello DLL world!", null, 0); 14 | } 15 | -------------------------------------------------------------------------------- /samples/05-dll/slim.ini: -------------------------------------------------------------------------------- 1 | name=dll 2 | modules=dll dllfuns 3 | 4 | ; The dll option changes the output file extension, 5 | ; and enables import library generation. 6 | dll=true 7 | 8 | ; The entry point uses the stdcall calling convention 9 | ; (it receives three arguments), so use the appropriate 10 | ; mangling. 11 | ; Alternatively we could've used pragma(mangle). 12 | entry=DllEntryPoint@12 13 | -------------------------------------------------------------------------------- /samples/06-dlluser/dlluser.d: -------------------------------------------------------------------------------- 1 | /// SlimD example of linking to a D DLL. 2 | module dlluser; 3 | 4 | /// We import the module from the DLL implementation which 5 | /// defines the DLL functions. However, we only import it - 6 | /// the module will not be compiled or linked with the DLL. 7 | /// This allows us to use the same declaration for both the 8 | /// DLL implementation and interface. Otherwise, we'd need to 9 | /// have a separate .di file, or use extern(C) declarations 10 | /// to declare the functions defined in the DLL. 11 | import dllfuns; 12 | 13 | extern(C) 14 | void start() 15 | { 16 | fun(); 17 | } 18 | pragma(startaddress, start); 19 | -------------------------------------------------------------------------------- /samples/06-dlluser/slim.ini: -------------------------------------------------------------------------------- 1 | name=dlluser 2 | 3 | ; Add the DLL source path to the module search path, 4 | ; so that we can import dllfuns.d to obtain the 5 | ; function declarations. 6 | dflags=-I..\05-dll\ 7 | 8 | ; Add the DLL import library, so the linker knows 9 | ; that the missing symbols are located in a DLL, 10 | ; and what the DLL name is. 11 | libs=..\05-dll\dll.lib 12 | -------------------------------------------------------------------------------- /slimbuild/slimbuild.d: -------------------------------------------------------------------------------- 1 | import core.runtime; 2 | 3 | import std.algorithm; 4 | import std.array; 5 | import std.conv; 6 | import std.exception; 7 | import std.file; 8 | import std.path; 9 | import std.process; 10 | import std.stdio; 11 | import std.string; 12 | 13 | import ae.sys.file; 14 | import ae.utils.aa; 15 | import ae.utils.array; 16 | import ae.utils.meta; 17 | import ae.utils.path; 18 | import ae.utils.sini; 19 | import ae.utils.text; 20 | 21 | struct Config 22 | { 23 | bool verbose; 24 | bool force; 25 | 26 | string name; 27 | string modules; 28 | string libs; 29 | bool console; 30 | bool dll; 31 | string entry = "start"; 32 | string dflags = "-release"; 33 | string compiler; 34 | string linker; 35 | string model = "32"; 36 | bool demangle; // demangle linker output 37 | 38 | struct LibPath 39 | { 40 | string[string] omf, coff32, coff64; 41 | } 42 | LibPath libPath; 43 | 44 | struct Tool 45 | { 46 | string command; 47 | string path; 48 | string[string] env, args; 49 | } 50 | 51 | struct Tools 52 | { 53 | Tool dmd = Tool("dmd" ); 54 | Tool ldc = Tool("ldc2" ); 55 | Tool unilink = Tool("ulink" ); 56 | Tool mslink = Tool("link" ); 57 | Tool crinkler = Tool("crinkler" ); 58 | Tool golink = Tool("golink" ); 59 | Tool watcom = Tool("wlink" ); 60 | Tool gcc = Tool("gcc" ); 61 | Tool ddemangle = Tool("ddemangle"); 62 | } 63 | Tools tools; 64 | } 65 | 66 | immutable Config config; 67 | immutable string[] inis; 68 | immutable string root; // Root dir of SlimD 69 | 70 | shared static this() 71 | { 72 | string slimIni = "slim.ini"; 73 | string[] cmdlineConfig = ["[]"]; 74 | 75 | auto args = Runtime.args[1..$]; 76 | for (size_t n=0; n m.defaultExtension(".d")).array; 185 | else 186 | { 187 | modules = [config.name ~ ".d"]; 188 | vlog("No 'modules' specified, implying from name: " ~ modules[0]); 189 | } 190 | 191 | string omf = "%s.omf.obj".format(config.name); 192 | string coff = "%s.coff.obj".format(config.name); 193 | string exe = "%s.%s".format(config.name, config.dll ? "dll" : "exe"); 194 | auto dflags = config.dflags.split(); 195 | auto libs = config.libs.split(); 196 | auto subsystem = config.console ? "CONSOLE" : "WINDOWS"; 197 | 198 | string[] sources = inis.dup.filter!exists.array; 199 | sources ~= thisExePath; 200 | sources ~= modules; 201 | 202 | T selectTool(T)(string name, T defaultValue = T.init) 203 | { 204 | if (!name) 205 | { 206 | vlog("No '%s' specified, defaulting to %s.".format(T.stringof.toLower(), defaultValue)); 207 | return defaultValue; 208 | } 209 | return to!T(name.split('-').camelCaseJoin()); 210 | } 211 | 212 | enum Compiler { dmd, ldc } 213 | auto compiler = selectTool!Compiler(config.compiler); 214 | 215 | if (config.verbose) 216 | final switch (compiler) 217 | { 218 | case Compiler.dmd: 219 | case Compiler.ldc: 220 | dflags ~= ["-v"]; 221 | break; 222 | } 223 | 224 | enum Linker { optlink, unilink, unilinkCoff, mslink, crinkler, golink, watcom, gcc } 225 | auto linker = selectTool!Linker(config.linker, config.model == "32" ? Linker.optlink : Linker.mslink); 226 | 227 | bool shouldBuild(string[] sources, string target) 228 | { 229 | if (config.force || sources.anyNewerThan(target)) 230 | return true; 231 | vlog(target ~ " up to date."); 232 | return false; 233 | } 234 | 235 | void needOmf() 236 | { 237 | if (shouldBuild(sources, omf)) 238 | final switch (compiler) 239 | { 240 | case Compiler.dmd: 241 | enforce(config.model == "32", "DMD can only generate 32-bit OMF files"); 242 | runTool(config.tools.dmd, 243 | dflags ~ 244 | modules ~ 245 | [ 246 | "-c", // Compile only, do not link 247 | "-betterC", // Disable Druntime helpers 248 | "-of" ~ omf, // Output file 249 | "-m32", // Generate 32-bit OMF file 250 | ] 251 | ); 252 | break; 253 | case Compiler.ldc: 254 | throw new Exception("%s cannot generate OMF object files".format(compiler)); 255 | } 256 | } 257 | 258 | void needCoff() 259 | { 260 | if (shouldBuild(sources, coff)) 261 | final switch (compiler) 262 | { 263 | case Compiler.dmd: 264 | runTool( 265 | config.tools.dmd, 266 | dflags ~ 267 | modules ~ 268 | [ 269 | "-c", // Compile only, do not link 270 | "-betterC", // Disable Druntime helpers 271 | "-m" ~ (config.model == "32" ? "32mscoff" : "64"), // Create COFF object file 272 | "-of" ~ coff, // Output file 273 | ] 274 | ); 275 | break; 276 | case Compiler.ldc: 277 | runTool( 278 | config.tools.ldc, 279 | dflags ~ 280 | modules ~ 281 | [ 282 | "-c", // Compile only, do not link 283 | "-m" ~ config.model, // Create COFF object file 284 | "-fdata-sections", 285 | "-ffunction-sections", 286 | "-Oz", // Smallest input file 287 | "-of=" ~ coff, // Output file 288 | ] 289 | ); 290 | break; 291 | } 292 | } 293 | 294 | string obj; 295 | 296 | final switch (linker) 297 | { 298 | case Linker.optlink: 299 | case Linker.unilink: 300 | case Linker.watcom: 301 | needOmf(); 302 | obj = omf; 303 | break; 304 | 305 | case Linker.unilinkCoff: 306 | case Linker.mslink: 307 | case Linker.crinkler: 308 | case Linker.golink: 309 | case Linker.gcc: 310 | needCoff(); 311 | obj = coff; 312 | break; 313 | } 314 | 315 | sources ~= obj; 316 | if (!shouldBuild(sources, exe)) 317 | return; 318 | 319 | final switch (linker) 320 | { 321 | case Linker.optlink: 322 | runTool( 323 | config.tools.dmd, 324 | dflags ~ 325 | obj ~ 326 | libs ~ 327 | [ 328 | "-L/SUBSYSTEM:" ~ subsystem, // Subsystem 329 | "-of" ~ exe, // Output file 330 | ] ~ 331 | // Library search paths 332 | config.libPath.omf.sortedValues.map!(path => "-L+" ~ path.includeTrailingPathSeparator).array ~ 333 | // Entry point 334 | (config.entry.length ? "-L/ENTRY:_" ~ config.entry : []) ~ 335 | (config.dll ? ["-L/IMPLIB"] : []) // Generate an import library 336 | ); 337 | break; 338 | 339 | case Linker.unilink: 340 | case Linker.unilinkCoff: 341 | runTool( 342 | config.tools.unilink, 343 | obj ~ 344 | libs ~ 345 | [ 346 | "-q", // Suppress banner 347 | "-GS:*=*", // Merge all sections 348 | "-Gh", // Strip unused PE directories 349 | "-ZX-", // Minimal DOS stub 350 | "-a" ~ (config.console ? 'p' : 'a'), // Subsystem 351 | // Target type 352 | "-Tp" ~ (config.dll ? 'd' : 'e') ~ (config.model == "64" ? "+" : ""), 353 | "-ZO" ~ exe, // Output file 354 | ] ~ 355 | // Entry point 356 | (config.entry.length ? ["-e" ~ (linker == Linker.unilink ? "_" : "") ~ config.entry] : []) ~ 357 | // Library search path 358 | (linker == Linker.unilink 359 | ? config.libPath.omf 360 | : config.model == "32" 361 | ? config.libPath.coff32 362 | : config.libPath.coff64) 363 | .sortedValues.map!(path => "-L" ~ path.excludeTrailingPathSeparator).array ~ 364 | // Generate an import library (in OMF/COFF format) 365 | (config.dll ? [linker == Linker.unilink ? "-Gi" : "-Gic"] : []) 366 | ); 367 | break; 368 | 369 | case Linker.mslink: 370 | runTool( 371 | config.tools.mslink, 372 | obj ~ 373 | libs ~ 374 | [ 375 | "/NOLOGO", // Suppress banner 376 | "/MERGE:.text=.slimd", // Merge code section into .slimd 377 | "/MERGE:.rdata=.slimd", // Merge read-only data section into .slimd 378 | "/MERGE:.data=.slimd", // Merge data section into .slimd 379 | "/SECTION:.slimd,ERW", // Set .slimd section flags 380 | "/IGNORE:4254", // Ignore "section 'section1' (offset) merged into 'section2' (offset) with different attributes" 381 | config.dll ? "/DLL" : "/FIXED", // DLL / Disable relocations 382 | 383 | "/SUBSYSTEM:" ~ subsystem, // Subsystem 384 | "/OUT:" ~ exe, // Output file 385 | ] ~ 386 | // Library search path 387 | (config.model == "32" 388 | ? config.libPath.coff32 389 | : config.libPath.coff64) 390 | .sortedValues.map!(path => "/LIBPATH:" ~ path.excludeTrailingPathSeparator).array ~ 391 | // Entry point 392 | (config.entry.length ? ["/ENTRY:" ~ config.entry] : []) 393 | ); 394 | break; 395 | 396 | case Linker.crinkler: 397 | enforce(!config.dll, "Crinkler does not support DLL files."); 398 | runTool( 399 | config.tools.crinkler, 400 | obj ~ 401 | libs ~ 402 | ["kernel32.lib"] ~ // kernel32.dll is always needed for Crinkler's GetProcAddress call 403 | [ 404 | "/COMPMODE:SLOW", // Use best compression 405 | "/ORDERTRIES:1000", // Try a thousand section order combinations 406 | "/UNSAFEIMPORT", // Save more space by assuming all DLLs will be present 407 | 408 | "/SUBSYSTEM:" ~ subsystem, // Subsystem 409 | "/OUT:" ~ exe, // Output file 410 | ] ~ 411 | // Library search path 412 | (config.model == "32" 413 | ? config.libPath.coff32 414 | : config.libPath.coff64) 415 | .sortedValues.I!(paths => 416 | paths.length == 0 ? [] : ["/LIBPATH:" ~ paths.map!excludeTrailingPathSeparator.join(";")]) ~ 417 | // Entry point 418 | (config.entry.length ? ["/ENTRY:" ~ config.entry] : []) 419 | ); 420 | break; 421 | 422 | case Linker.golink: 423 | runTool( 424 | config.tools.golink, 425 | obj ~ 426 | libs.map!(lib => lib.setExtension(".dll")).array ~ 427 | (config.verbose ? ["/files"] : []) ~ 428 | (config.console ? ["/console"] : []) ~ // Subsystem 429 | [ 430 | "/mix", 431 | "/fo", exe, // Output file 432 | ] ~ 433 | // Entry point 434 | (config.entry.length ? ["/entry", "_" ~ config.entry] : []) 435 | ); 436 | break; 437 | 438 | case Linker.watcom: 439 | runTool( 440 | config.tools.watcom, 441 | [ 442 | "name", config.name, 443 | "file", obj, 444 | "system", config.console ? "nt" : "nt_win", 445 | "option", "NORELOCS", 446 | ] ~ 447 | libs.map!(lib => ["library", lib]).join 448 | ); 449 | break; 450 | 451 | case Linker.gcc: 452 | runTool( 453 | config.tools.gcc, 454 | obj ~ 455 | [ 456 | "-Wl,--gc-sections", // Garbage-collect sections. Doesn't seem to be implemented for COFF/PE. 457 | "-nostdlib", // Inhibit linking with libc 458 | 459 | "-o", exe, // Output file 460 | "-Wl,-subsystem," ~ 461 | subsystem.toLower(), // Subsystem 462 | ] ~ 463 | (config.entry.length ? ["-Wl,-e_" ~ config.entry] : []) ~ 464 | libs.map!(lib => "-l" ~ lib.stripExtension()).array() 465 | ); 466 | break; 467 | } 468 | } 469 | --------------------------------------------------------------------------------