Newer
Older
dub_jkp / source / dub / platform.d
@WebFreak001 WebFreak001 on 4 Feb 2023 8 KB fix typo(s)
  1. /**
  2. Build platform identification and specification matching.
  3.  
  4. This module is useful for determining the build platform for a certain
  5. machine and compiler invocation. Example applications include classifying
  6. CI slave machines.
  7.  
  8. It also contains means to match build platforms against a platform
  9. specification string as used in package recipes.
  10.  
  11. Copyright: © 2012-2017 rejectedsoftware e.K.
  12. License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file.
  13. Authors: Sönke Ludwig
  14. */
  15. module dub.platform;
  16.  
  17. import std.array;
  18.  
  19. // archCheck, compilerCheck, and platformCheck are used below and in
  20. // generatePlatformProbeFile, so they've been extracted into these strings
  21. // that can be reused.
  22. // Try to not use phobos in the probes to avoid long import times.
  23. /// private
  24. enum string platformCheck = q{
  25. string[] ret;
  26. version(Windows) ret ~= "windows";
  27. version(linux) ret ~= "linux";
  28. version(Posix) ret ~= "posix";
  29. version(OSX) ret ~= ["osx", "darwin"];
  30. version(iOS) ret ~= ["ios", "darwin"];
  31. version(TVOS) ret ~= ["tvos", "darwin"];
  32. version(WatchOS) ret ~= ["watchos", "darwin"];
  33. version(FreeBSD) ret ~= "freebsd";
  34. version(OpenBSD) ret ~= "openbsd";
  35. version(NetBSD) ret ~= "netbsd";
  36. version(DragonFlyBSD) ret ~= "dragonflybsd";
  37. version(BSD) ret ~= "bsd";
  38. version(Solaris) ret ~= "solaris";
  39. version(AIX) ret ~= "aix";
  40. version(Haiku) ret ~= "haiku";
  41. version(SkyOS) ret ~= "skyos";
  42. version(SysV3) ret ~= "sysv3";
  43. version(SysV4) ret ~= "sysv4";
  44. version(Hurd) ret ~= "hurd";
  45. version(Android) ret ~= "android";
  46. version(Cygwin) ret ~= "cygwin";
  47. version(MinGW) ret ~= "mingw";
  48. version(PlayStation4) ret ~= "playstation4";
  49. version(WebAssembly) ret ~= "wasm";
  50. return ret;
  51. };
  52.  
  53. /// private
  54. enum string archCheck = q{
  55. string[] ret;
  56. version(X86) ret ~= "x86";
  57. // Hack: see #1535
  58. // Makes "x86_omf" available as a platform specifier in the package recipe
  59. version(X86) version(CRuntime_DigitalMars) ret ~= "x86_omf";
  60. // Hack: see #1059
  61. // When compiling with --arch=x86_mscoff build_platform.architecture is equal to ["x86"] and canFind below is false.
  62. // This hack prevents unnecessary warning 'Failed to apply the selected architecture x86_mscoff. Got ["x86"]'.
  63. // And also makes "x86_mscoff" available as a platform specifier in the package recipe
  64. version(X86) version(CRuntime_Microsoft) ret ~= "x86_mscoff";
  65. version(X86_64) ret ~= "x86_64";
  66. version(ARM) ret ~= "arm";
  67. version(AArch64) ret ~= "aarch64";
  68. version(ARM_Thumb) ret ~= "arm_thumb";
  69. version(ARM_SoftFloat) ret ~= "arm_softfloat";
  70. version(ARM_HardFloat) ret ~= "arm_hardfloat";
  71. version(PPC) ret ~= "ppc";
  72. version(PPC_SoftFP) ret ~= "ppc_softfp";
  73. version(PPC_HardFP) ret ~= "ppc_hardfp";
  74. version(PPC64) ret ~= "ppc64";
  75. version(IA64) ret ~= "ia64";
  76. version(MIPS) ret ~= "mips";
  77. version(MIPS32) ret ~= "mips32";
  78. version(MIPS64) ret ~= "mips64";
  79. version(MIPS_O32) ret ~= "mips_o32";
  80. version(MIPS_N32) ret ~= "mips_n32";
  81. version(MIPS_O64) ret ~= "mips_o64";
  82. version(MIPS_N64) ret ~= "mips_n64";
  83. version(MIPS_EABI) ret ~= "mips_eabi";
  84. version(MIPS_NoFloat) ret ~= "mips_nofloat";
  85. version(MIPS_SoftFloat) ret ~= "mips_softfloat";
  86. version(MIPS_HardFloat) ret ~= "mips_hardfloat";
  87. version(SPARC) ret ~= "sparc";
  88. version(SPARC_V8Plus) ret ~= "sparc_v8plus";
  89. version(SPARC_SoftFP) ret ~= "sparc_softfp";
  90. version(SPARC_HardFP) ret ~= "sparc_hardfp";
  91. version(SPARC64) ret ~= "sparc64";
  92. version(S390) ret ~= "s390";
  93. version(S390X) ret ~= "s390x";
  94. version(HPPA) ret ~= "hppa";
  95. version(HPPA64) ret ~= "hppa64";
  96. version(SH) ret ~= "sh";
  97. version(SH64) ret ~= "sh64";
  98. version(Alpha) ret ~= "alpha";
  99. version(Alpha_SoftFP) ret ~= "alpha_softfp";
  100. version(Alpha_HardFP) ret ~= "alpha_hardfp";
  101. return ret;
  102. };
  103.  
  104. /// private
  105. enum string compilerCheck = q{
  106. version(DigitalMars) return "dmd";
  107. else version(GNU) return "gdc";
  108. else version(LDC) return "ldc";
  109. else version(SDC) return "sdc";
  110. else return null;
  111. };
  112.  
  113. /** Determines the full build platform used for the current build.
  114.  
  115. Note that the `BuildPlatform.compilerBinary` field will be left empty.
  116.  
  117. See_Also: `determinePlatform`, `determineArchitecture`, `determineCompiler`
  118. */
  119. BuildPlatform determineBuildPlatform()
  120. {
  121. BuildPlatform ret;
  122. ret.platform = determinePlatform();
  123. ret.architecture = determineArchitecture();
  124. ret.compiler = determineCompiler();
  125. ret.frontendVersion = __VERSION__;
  126. return ret;
  127. }
  128.  
  129.  
  130. /** Returns a list of platform identifiers that apply to the current
  131. build.
  132.  
  133. Example results are `["windows"]` or `["posix", "osx"]`. The identifiers
  134. correspond to the compiler defined version constants built into the
  135. language, except that they are converted to lower case.
  136.  
  137. See_Also: `determineBuildPlatform`
  138. */
  139. string[] determinePlatform()
  140. {
  141. mixin(platformCheck);
  142. }
  143.  
  144. /** Returns a list of architecture identifiers that apply to the current
  145. build.
  146.  
  147. Example results are `["x86_64"]` or `["arm", "arm_softfloat"]`. The
  148. identifiers correspond to the compiler defined version constants built into
  149. the language, except that they are converted to lower case.
  150.  
  151. See_Also: `determineBuildPlatform`
  152. */
  153. string[] determineArchitecture()
  154. {
  155. mixin(archCheck);
  156. }
  157.  
  158. /** Determines the canonical compiler name used for the current build.
  159.  
  160. The possible values currently are "dmd", "gdc", "ldc" or "sdc". If an
  161. unknown compiler is used, this function will return an empty string.
  162.  
  163. See_Also: `determineBuildPlatform`
  164. */
  165. string determineCompiler()
  166. {
  167. mixin(compilerCheck);
  168. }
  169.  
  170. /** Matches a platform specification string against a build platform.
  171.  
  172. Specifications are build upon the following scheme, where each component
  173. is optional (indicated by []), but the order is obligatory:
  174. "[-platform][-architecture][-compiler]"
  175.  
  176. So the following strings are valid specifications: `"-windows-x86-dmd"`,
  177. `"-dmd"`, `"-arm"`, `"-arm-dmd"`, `"-windows-dmd"`
  178.  
  179. Params:
  180. platform = The build platform to match against the platform specification
  181. specification = The specification being matched. It must either be an
  182. empty string or start with a dash.
  183.  
  184. Returns:
  185. `true` if the given specification matches the build platform, `false`
  186. otherwise. Using an empty string as the platform specification will
  187. always result in a match.
  188. */
  189. bool matchesSpecification(in BuildPlatform platform, const(char)[] specification)
  190. {
  191. import std.string : chompPrefix, format;
  192. import std.algorithm : canFind, splitter;
  193. import std.exception : enforce;
  194.  
  195. if (specification.empty) return true;
  196. if (platform == BuildPlatform.any) return true;
  197.  
  198. auto splitted = specification.chompPrefix("-").splitter('-');
  199. enforce(!splitted.empty, format("Platform specification, if present, must not be empty: \"%s\"", specification));
  200.  
  201. if (platform.platform.canFind(splitted.front)) {
  202. splitted.popFront();
  203. if (splitted.empty)
  204. return true;
  205. }
  206. if (platform.architecture.canFind(splitted.front)) {
  207. splitted.popFront();
  208. if (splitted.empty)
  209. return true;
  210. }
  211. if (platform.compiler == splitted.front) {
  212. splitted.popFront();
  213. enforce(splitted.empty, "No valid specification! The compiler has to be the last element: " ~ specification);
  214. return true;
  215. }
  216. return false;
  217. }
  218.  
  219. ///
  220. unittest {
  221. auto platform = BuildPlatform(["posix", "linux"], ["x86_64"], "dmd");
  222. assert(platform.matchesSpecification(""));
  223. assert(platform.matchesSpecification("posix"));
  224. assert(platform.matchesSpecification("linux"));
  225. assert(platform.matchesSpecification("linux-dmd"));
  226. assert(platform.matchesSpecification("linux-x86_64-dmd"));
  227. assert(platform.matchesSpecification("x86_64"));
  228. assert(!platform.matchesSpecification("windows"));
  229. assert(!platform.matchesSpecification("ldc"));
  230. assert(!platform.matchesSpecification("windows-dmd"));
  231.  
  232. // Before PR#2279, a leading '-' was required
  233. assert(platform.matchesSpecification("-x86_64"));
  234. }
  235.  
  236. /// Represents a platform a package can be build upon.
  237. struct BuildPlatform {
  238. /// Special constant used to denote matching any build platform.
  239. enum any = BuildPlatform(null, null, null, null, -1);
  240.  
  241. /// Platform identifiers, e.g. ["posix", "windows"]
  242. string[] platform;
  243. /// CPU architecture identifiers, e.g. ["x86", "x86_64"]
  244. string[] architecture;
  245. /// Canonical compiler name e.g. "dmd"
  246. string compiler;
  247. /// Compiler binary name e.g. "ldmd2"
  248. string compilerBinary;
  249. /// Compiled frontend version (e.g. `2067` for frontend versions 2.067.x)
  250. int frontendVersion;
  251. /// Compiler version e.g. "1.11.0"
  252. string compilerVersion;
  253. /// Frontend version string from frontendVersion
  254. /// e.g: 2067 => "2.067"
  255. string frontendVersionString() const
  256. {
  257. import std.format : format;
  258.  
  259. const maj = frontendVersion / 1000;
  260. const min = frontendVersion % 1000;
  261. return format("%d.%03d", maj, min);
  262. }
  263. ///
  264. unittest
  265. {
  266. BuildPlatform bp;
  267. bp.frontendVersion = 2067;
  268. assert(bp.frontendVersionString == "2.067");
  269. }
  270.  
  271. /// Checks to see if platform field contains windows
  272. bool isWindows() const {
  273. import std.algorithm : canFind;
  274. return this.platform.canFind("windows");
  275. }
  276. ///
  277. unittest {
  278. BuildPlatform bp;
  279. bp.platform = ["windows"];
  280. assert(bp.isWindows);
  281. bp.platform = ["posix"];
  282. assert(!bp.isWindows);
  283. }
  284. }