1 module gccbuild.config; 2 3 import gccbuild, scriptlike, painlessjson, std.json; 4 5 BuildMode mode = BuildMode.all; 6 Path buildConfig, sourceConfig, logFilePath; 7 string buildTriplet = "x86_64-unknown-linux-gnu"; 8 Path mirrorFile = "mirrors.json"; 9 bool verifyCachedSources = true; 10 bool forceDownload = false; 11 bool forceExtract = false; 12 size_t numCPUs = 1; 13 Path cacheDir; 14 string[string] buildVariables; 15 string[string] cmdVariables; 16 bool skipStripLibraries = false; 17 bool skipStripBinaries = false; 18 bool keepBuildFiles = false; 19 string hostStripCMD; 20 string targetStripCMD; 21 string[] patchDirsCMD; 22 string gdcSourcePath; 23 string targetGCCCMD; 24 25 @property string targetGCC() 26 { 27 if (targetGCCCMD.empty) 28 return build.target ~ "-gcc"; 29 else 30 return targetGCCCMD; 31 } 32 33 @property string hostStrip() 34 { 35 if (!hostStripCMD.empty) 36 return hostStripCMD; 37 else if (build.type == ToolchainType.native) 38 return "strip"; 39 else 40 return build.host ~ "-strip"; 41 } 42 43 @property string targetStrip() 44 { 45 if (!targetStripCMD.empty) 46 return hostStripCMD; 47 else if (build.type == ToolchainType.native) 48 return "strip"; 49 else 50 return build.target ~ "-strip"; 51 } 52 53 Path[] patchDirectories; 54 55 struct CMDBuildOverwrites 56 { 57 string host, target; 58 string gccFile, gccSuburl, gccMD5; 59 Nullable!ToolchainType type; 60 } 61 62 CMDBuildOverwrites cmdOverwrites; 63 64 @property Path sysrootDirStage1() 65 { 66 return toolchainDirStage1 ~ "sysroot"; 67 } 68 69 @property Path binDirStage1() 70 { 71 return toolchainDirStage1 ~ "bin"; 72 } 73 74 @property Path toolchainDirStage1() 75 { 76 return installDir ~ "stage1"; 77 } 78 79 @property Path sysrootDirWithPrefix() 80 { 81 return sysrootDir ~ build.relativeSysrootPrefix; 82 } 83 84 @property Path sysrootDir() 85 { 86 return toolchainDir ~ "sysroot"; 87 } 88 89 @property Path binDir() 90 { 91 return toolchainDir ~ "bin"; 92 } 93 94 @property Path toolchainDir() 95 { 96 return installDir ~ build.target; 97 } 98 99 @property Path hostlibDir() 100 { 101 return installDir ~ "host"; 102 } 103 104 @property Path mainPatchDir() 105 { 106 return cacheDir ~ "patches"; 107 } 108 109 @property Path installDir() 110 { 111 return cacheDir ~ "install"; 112 } 113 114 @property Path extractDir() 115 { 116 return cacheDir ~ "src"; 117 } 118 119 @property Path buildDir() 120 { 121 return cacheDir ~ "build"; 122 } 123 124 @property Path downloadDir() 125 { 126 return cacheDir ~ "download"; 127 } 128 129 MainConfig build; 130 string[][string] mirrors; 131 132 void setDefaultPaths() 133 { 134 cacheDir = Path("cache").absolutePath; 135 logFilePath = Path("build.log").absolutePath; 136 } 137 138 void loadMirrors() 139 { 140 startSectionLog("Loading mirror information"); 141 try 142 mirrors = fromJSON!(typeof(mirrors))(mirrorFile.readText().parseJSON()); 143 catch (Exception e) 144 failc("Couldn't load mirror file ", mirrorFile, ": ", e); 145 endSectionLog(); 146 } 147 148 void loadSourceConfig(Path file) 149 { 150 startSectionLog("Loading build sources configuration"); 151 try 152 build = fromJSON!MainConfig(file.readText().parseJSON()); 153 catch (Exception e) 154 failc("Couldn't load build sources configuration ", file, ": ", e); 155 endSectionLog(); 156 } 157 158 void loadBuildConfig(Path file) 159 { 160 startSectionLog("Loading build configuration"); 161 BuildConfig commands; 162 try 163 commands = fromJSON!BuildConfig(file.readText().parseJSON()); 164 catch (Exception e) 165 failc("Couldn't load build configuration ", file, ": ", e); 166 167 build.include(commands); 168 build.include(cmdOverwrites); 169 170 endSectionLog(); 171 } 172 173 void setupBuildVariables() 174 { 175 startSectionLog("Setting build variables"); 176 177 buildVariables["NUM_CPU"] = to!string(numCPUs); 178 buildVariables["BUILD"] = buildTriplet; 179 buildVariables["HOST"] = build.host; 180 buildVariables["TARGET"] = build.target; 181 buildVariables["ARCH"] = build.arch; 182 buildVariables["DIR_GMP_INSTALL"] = (hostlibDir ~ build.gmp.baseDirName).toString(); 183 buildVariables["DIR_MPFR_INSTALL"] = (hostlibDir ~ build.mpfr.baseDirName).toString(); 184 buildVariables["DIR_MPC_INSTALL"] = (hostlibDir ~ build.mpc.baseDirName).toString(); 185 buildVariables["DIR_TOOLCHAIN"] = toolchainDir.toString(); 186 buildVariables["DIR_SYSROOT"] = sysrootDir.toString(); 187 buildVariables["DIR_SYSROOT_PREFIX"] = build.sysrootPrefix; 188 buildVariables["DIR_SYSROOT_WITH_PREFIX"] = sysrootDirWithPrefix.toString(); 189 buildVariables["DIR_TOOLCHAIN_STAGE1"] = toolchainDirStage1.toString(); 190 buildVariables["DIR_SYSROOT_STAGE1"] = sysrootDirStage1.toString(); 191 buildVariables["DIR_SYSROOT_STAGE1_WITH_PREFIX"] = ( 192 sysrootDirStage1 ~ build.relativeSysrootPrefix).toString(); 193 buildVariables["TARGET_GCC"] = build.target ~ "-gcc"; 194 195 // overwrite from build.json 196 static void addConstants(string[string] cst) 197 { 198 foreach (key, val; cst) 199 { 200 buildVariables[key] = val; 201 } 202 } 203 204 addConstants(build.constants); 205 final switch (build.type) 206 { 207 case ToolchainType.cross: 208 addConstants(build.constantsCross); 209 break; 210 case ToolchainType.native: 211 addConstants(build.constantsNative); 212 break; 213 case ToolchainType.cross_native: 214 addConstants(build.constantsCrossNative); 215 break; 216 case ToolchainType.canadian: 217 addConstants(build.constantsCanadian); 218 break; 219 } 220 221 // overwrites from cmd 222 foreach (key, val; cmdVariables) 223 { 224 buildVariables[key] = val; 225 } 226 227 foreach (key, val; buildVariables) 228 { 229 yap(key, "=", val); 230 } 231 endSectionLog(); 232 233 // Setup patch directories 234 startSectionLog("Setting patch directories"); 235 236 static void addConfPatchDirs(string[] dirs) 237 { 238 foreach (dir; dirs) 239 { 240 // Make sure relative Paths are relative to the build config specifying them 241 auto path = Path(dir); 242 if (!path.isAbsolute) 243 path = path.absolutePath(buildConfig.dirName); 244 patchDirectories ~= path; 245 } 246 } 247 248 patchDirectories ~= mainPatchDir; 249 addConfPatchDirs(build.localPatchDirs); 250 final switch (build.type) 251 { 252 case ToolchainType.cross: 253 addConfPatchDirs(build.localPatchDirsCross); 254 break; 255 case ToolchainType.native: 256 addConfPatchDirs(build.localPatchDirsNative); 257 break; 258 case ToolchainType.cross_native: 259 addConfPatchDirs(build.localPatchDirsCrossNative); 260 break; 261 case ToolchainType.canadian: 262 addConfPatchDirs(build.localPatchDirsCanadian); 263 break; 264 } 265 foreach (dir; patchDirsCMD) 266 patchDirectories ~= Path(dir); 267 268 endSectionLog(); 269 } 270 271 void dumpConfiguration() 272 { 273 startSection("Dumping configuration"); 274 writeBulletPoint(mixin(interp!"Type: ${build.type} Target type: ${build.targetType}")); 275 writeBulletPoint(mixin(interp!"Build: ${buildTriplet}")); 276 writeBulletPoint(mixin(interp!"Host: ${build.host}")); 277 writeBulletPoint(mixin(interp!"Target: ${build.target}")); 278 if (build.targetType == HostType.linux) 279 writeBulletPoint(mixin(interp!"Kernel ARCH: ${build.arch}")); 280 endSection(); 281 } 282 283 auto namedFields(T, A...)(ref T instance) 284 { 285 static string generateMixin(string[] fields) 286 { 287 string result = "return only("; 288 foreach (i, entry; fields) 289 { 290 if (i != 0) 291 result ~= ", "; 292 result ~= "tuple!(\"name\", \"value\")(\"" ~ entry ~ "\", cast(Component)instance." ~ entry ~ ")"; 293 } 294 result ~= ");"; 295 return result; 296 } 297 298 mixin(generateMixin([A])); 299 } 300 301 struct BuildConfig 302 { 303 string host, target, arch; 304 string[string] constants; 305 @SerializedName("constants_native") string[string] constantsNative; 306 @SerializedName("constants_cross") string[string] constantsCross; 307 @SerializedName("constants_cross_native") string[string] constantsCrossNative; 308 @SerializedName("constants_canadian") string[string] constantsCanadian; 309 310 @SerializedName("sysroot_prefix") string sysrootPrefix = "/"; 311 @SerializedName("patch_dirs") string[] localPatchDirs; 312 @SerializedName("patch_dirs_native") string[] localPatchDirsNative; 313 @SerializedName("patch_dirs_cross") string[] localPatchDirsCross; 314 @SerializedName("patch_dirs_cross_native") string[] localPatchDirsCrossNative; 315 @SerializedName("patch_dirs_canadian") string[] localPatchDirsCanadian; 316 317 string type = ""; 318 319 BuildCommand gmp, mpfr, mpc, linux, binutils, w32api, gcc; 320 GlibcBuildCommand glibc; 321 CleanupCommand cleanup; 322 @SerializedName("cleanup_cross") CleanupCommand cleanupCross; 323 @SerializedName("cleanup_cross_native") CleanupCommand cleanupCrossNative; 324 @SerializedName("cleanup_native") CleanupCommand cleanupNative; 325 @SerializedName("cleanup_canadian") CleanupCommand cleanupCanadian; 326 @SerializedName("gcc_stage1") BuildCommand gccStage1; 327 } 328 329 struct MainConfig 330 { 331 @SerializeIgnore string host; 332 @SerializeIgnore string target; 333 @SerializeIgnore string arch; 334 @SerializeIgnore string[string] constants; 335 @SerializeIgnore string[string] constantsNative; 336 @SerializeIgnore string[string] constantsCross; 337 @SerializeIgnore string[string] constantsCrossNative; 338 @SerializeIgnore string[string] constantsCanadian; 339 340 @SerializeIgnore MultilibEntry[] multilibs; 341 @SerializeIgnore string sysrootPrefix; 342 @property string relativeSysrootPrefix() 343 { 344 return sysrootPrefix.relativePath("/"); 345 } 346 347 @SerializeIgnore ToolchainType type; 348 @SerializeIgnore string[] localPatchDirs; 349 @SerializeIgnore string[] localPatchDirsNative; 350 @SerializeIgnore string[] localPatchDirsCross; 351 @SerializeIgnore string[] localPatchDirsCrossNative; 352 @SerializeIgnore string[] localPatchDirsCanadian; 353 354 Component mpc, mpfr, gmp, binutils, linux, w32api; 355 GCCComponent gcc; 356 GlibcComponent glibc; 357 // This is a special component: only a addon for glibc, don't build individually 358 @SerializedName("glibc_ports") Component glibcPorts; 359 360 @SerializeIgnore CleanupCommand cleanup; 361 @SerializeIgnore CleanupCommand cleanupCross; 362 @SerializeIgnore CleanupCommand cleanupCrossNative; 363 @SerializeIgnore CleanupCommand cleanupNative; 364 @SerializeIgnore CleanupCommand cleanupCanadian; 365 @property CleanupCommand variantCleanup() 366 { 367 final switch (type) 368 { 369 case ToolchainType.native: 370 return cleanupNative; 371 case ToolchainType.canadian: 372 return cleanupCanadian; 373 case ToolchainType.cross: 374 return cleanupCross; 375 case ToolchainType.cross_native: 376 return cleanupCrossNative; 377 } 378 } 379 380 @property HostType targetType() 381 { 382 if (target.toLower().canFind("mingw")) 383 return HostType.mingw; 384 else 385 return HostType.linux; 386 } 387 388 @property componentRange() 389 { 390 return this.namedFields!(MainConfig, "mpc", "mpfr", "gmp", "glibc", 391 "binutils", "linux", "w32api", "gcc"); 392 } 393 394 @property configuredComponents() 395 { 396 return this.componentRange.filter!(a => a.value.isInConfig); 397 } 398 399 void include(BuildConfig config) 400 { 401 host = config.host; 402 target = config.target; 403 arch = config.arch; 404 constants = config.constants; 405 constantsNative = config.constantsNative; 406 constantsCross = config.constantsCross; 407 constantsCrossNative = config.constantsCrossNative; 408 constantsCanadian = config.constantsCanadian; 409 sysrootPrefix = config.sysrootPrefix; 410 localPatchDirs = config.localPatchDirs; 411 localPatchDirsNative = config.localPatchDirsNative; 412 localPatchDirsCross = config.localPatchDirsCross; 413 localPatchDirsCrossNative = config.localPatchDirsCrossNative; 414 localPatchDirsCanadian = config.localPatchDirsCanadian; 415 cleanup = config.cleanup; 416 cleanupNative = config.cleanupNative; 417 cleanupCanadian = config.cleanupCanadian; 418 cleanupCross = config.cleanupCross; 419 cleanupCrossNative = config.cleanupCrossNative; 420 421 void trySet(Component comp, BuildCommand com) 422 { 423 if (comp) 424 comp._mainBuildCommand = com; 425 } 426 427 trySet(mpc, config.mpc); 428 trySet(mpfr, config.mpfr); 429 trySet(gmp, config.gmp); 430 trySet(glibc, config.glibc); 431 trySet(binutils, config.binutils); 432 trySet(linux, config.linux); 433 trySet(w32api, config.w32api); 434 if (gcc) 435 { 436 gcc._mainBuildCommand = config.gcc; 437 gcc._stage1BuildCommand = config.gccStage1; 438 } 439 } 440 441 void include(CMDBuildOverwrites overwrite) 442 { 443 void overwriteIfSet(ref string oldVal, string newVal) 444 { 445 if (!newVal.empty) 446 oldVal = newVal; 447 } 448 449 overwriteIfSet(host, cmdOverwrites.host); 450 overwriteIfSet(target, cmdOverwrites.target); 451 overwriteIfSet(gcc.file, cmdOverwrites.gccFile); 452 overwriteIfSet(gcc.suburl, cmdOverwrites.gccSuburl); 453 overwriteIfSet(gcc.md5, cmdOverwrites.gccMD5); 454 455 if (build.target == build.host) 456 { 457 if (build.host == buildTriplet) 458 build.type = ToolchainType.native; 459 else 460 build.type = ToolchainType.cross_native; 461 } 462 else 463 { 464 if (build.host == buildTriplet) 465 build.type = ToolchainType.cross; 466 else 467 build.type = ToolchainType.canadian; 468 } 469 470 if (!overwrite.type.isNull) 471 type = overwrite.type; 472 } 473 } 474 475 class Component 476 { 477 private: 478 BuildCommand _mainBuildCommand; 479 480 public: 481 482 string file, url, md5; 483 // Total url is url | mirror ~ suburl | mirror ~ filename 484 string suburl; 485 486 @property BuildCommand mainBuildCommand() 487 { 488 return _mainBuildCommand; 489 } 490 491 @SerializeIgnore bool wasExtracted = false; 492 493 @property Path localFile() 494 { 495 return downloadDir ~this.file; 496 } 497 498 @property Path baseDirName() 499 { 500 return Path(file.stripExt.stripExt); 501 } 502 503 Path getSourceFile(Path relFile) 504 { 505 return sourceFolder ~ relFile; 506 } 507 508 @property Path configureFile() 509 { 510 return getSourceFile(Path("configure")); 511 } 512 513 @property Path sourceFolder() 514 { 515 return extractDir ~ baseDirName; 516 } 517 518 @property Path buildFolder() 519 { 520 return buildDir ~ baseDirName; 521 } 522 } 523 524 // Whether this component is specified in config file 525 @property bool isInConfig(Component c) 526 { 527 return c !is null && c.mainBuildCommand !is null; 528 } 529 530 @property bool hasBuildCommands(Component c) 531 { 532 return c.isInConfig && c.mainBuildCommand.matchesBuildType; 533 } 534 535 class GlibcComponent : Component 536 { 537 public: 538 override @property GlibcBuildCommand mainBuildCommand() 539 { 540 return cast(GlibcBuildCommand) _mainBuildCommand; 541 } 542 } 543 544 class GCCComponent : Component 545 { 546 private: 547 BuildCommand _stage1BuildCommand; 548 549 public: 550 @property BuildCommand stage1BuildCommand() 551 { 552 return _stage1BuildCommand; 553 } 554 } 555 556 enum ToolchainType 557 { 558 native, 559 cross, 560 cross_native, 561 canadian 562 } 563 564 enum HostType 565 { 566 mingw, 567 linux 568 } 569 570 enum BuildMode 571 { 572 all, 573 download, 574 extract, 575 patch 576 } 577 578 class BuildCommand 579 { 580 string[] commands; 581 string[] variants; 582 } 583 584 @property bool matchesBuildType(BuildCommand cmd) 585 { 586 if (!cmd) 587 return false; 588 589 if (cmd.variants.empty) 590 return true; 591 592 foreach (variant; cmd.variants) 593 { 594 if (variant == to!string(build.type)) 595 return true; 596 } 597 return false; 598 } 599 600 class GlibcBuildCommand : BuildCommand 601 { 602 @SerializedName("multi_commands") string[][] multiCommands; 603 } 604 605 class CleanupCommand : BuildCommand 606 { 607 @SerializedName("strip_target") string[] stripTarget; 608 @SerializedName("strip_host") string[] stripHost; 609 string[] remove; 610 } 611 612 struct MultilibEntry 613 { 614 string gccFolder; 615 string args; 616 string osFolder; 617 618 @property bool isDefaultLib() 619 { 620 return gccFolder == "."; 621 } 622 }