1 module reggae.rules.common; 2 3 4 import reggae.build; 5 import reggae.config: projectPath; 6 import reggae.ctaa; 7 import reggae.types; 8 import std.algorithm; 9 import std.path; 10 import std.array: array; 11 12 version(Windows) { 13 immutable objExt = ".obj"; 14 immutable exeExt = ".exe"; 15 } else { 16 immutable objExt = ".o"; 17 immutable exeExt = ""; 18 } 19 20 package string objFileName(in string srcFileName) @safe pure nothrow { 21 import std.path: stripExtension, defaultExtension, isRooted, stripDrive; 22 immutable localFileName = srcFileName.isRooted 23 ? srcFileName.stripDrive[1..$] 24 : srcFileName; 25 return localFileName.stripExtension.defaultExtension(objExt); 26 } 27 28 29 /** 30 This template function exists so as to be referenced in a reggaefile.d 31 at top-level without being called via $(D alias). That way it can be 32 named and used in a further $(D Target) definition without the need to 33 define a function returning $(D Build). 34 This function gets the source files to be compiled at runtime by searching 35 for source files in the given directories, adding files and filtering 36 as appropriate by the parameters given in $(D sources), its first compile-time 37 parameter. The other parameters are self-explanatory. 38 39 This function returns a list of targets that are the result of compiling 40 source files written in the supported languages. The $(Sources) function 41 can be used to specify source directories and source files, as well as 42 a filter function to select those files that are actually wanted. 43 */ 44 Target[] targetsFromSources(alias sourcesFunc = Sources!(), 45 Flags flags = Flags(), 46 ImportPaths includes = ImportPaths(), 47 StringImportPaths stringImports = StringImportPaths(), 48 )() { 49 50 import std.exception: enforce; 51 import std.file; 52 import std.path: buildNormalizedPath, buildPath; 53 import std.array: array; 54 import std.traits: isCallable; 55 56 auto srcs = sourcesFunc(); 57 58 DirEntry[] modules; 59 foreach(dir; srcs.dirs.value.map!(a => buildPath(projectPath, a))) { 60 enforce(isDir(dir), dir ~ " is not a directory name"); 61 auto entries = dirEntries(dir, SpanMode.depth); 62 auto normalised = entries.map!(a => DirEntry(buildNormalizedPath(a))); 63 64 modules ~= normalised.filter!(a => !a.isDir).array; 65 } 66 67 foreach(module_; srcs.files.value) 68 modules ~= DirEntry(buildNormalizedPath(buildPath(projectPath, module_))); 69 70 const srcFiles = modules. 71 map!(a => a.name.removeProjectPath). 72 filter!(srcs.filterFunc). 73 array; 74 75 const dSrcs = srcFiles.filter!(a => a.getLanguage == Language.D).array; 76 auto otherSrcs = srcFiles.filter!(a => a.getLanguage != Language.D); 77 78 import reggae.rules.d: objectFiles; 79 return objectFiles(dSrcs, flags.value, ["."] ~ includes.value, stringImports.value) ~ 80 otherSrcs.map!(a => objectFile(a, flags.value, includes.value)).array; 81 } 82 83 @safe: 84 85 string removeProjectPath(in string path) pure { 86 import std.path: relativePath, absolutePath; 87 return path.absolutePath.relativePath(projectPath.absolutePath); 88 } 89 90 /** 91 An object file, typically from one source file in a certain language 92 (although for D the default is a whole package. The language is determined 93 by the file extension of the file(s) passed in. 94 */ 95 Target objectFile(in string srcFileName, 96 in string flags = "", 97 in string[] includePaths = [], 98 in string[] stringImportPaths = [], 99 in string projDir = "$project") pure { 100 101 const cmd = compileCommand(srcFileName, flags, includePaths, stringImportPaths, projDir); 102 return Target(srcFileName.objFileName, cmd, [Target(srcFileName)]); 103 } 104 105 106 Command compileCommand(in string srcFileName, 107 in string flags = "", 108 in string[] includePaths = [], 109 in string[] stringImportPaths = [], 110 in string projDir = "$project") pure { 111 auto includeParams = includePaths.map!(a => "-I" ~ buildPath(projDir, a)).array; 112 auto flagParams = flags.splitter.array; 113 immutable language = getLanguage(srcFileName); 114 115 auto params = [assocEntry("includes", includeParams), 116 assocEntry("flags", flagParams)]; 117 118 if(language == Language.D) 119 params ~= assocEntry("stringImports", stringImportPaths.map!(a => "-J" ~ buildPath(projDir, a)).array); 120 121 params ~= assocEntry("DEPFILE", ["$out.dep"]); 122 123 return Command(CommandType.compile, assocList(params)); 124 } 125 126 enum Language { 127 C, 128 Cplusplus, 129 D, 130 unknown, 131 } 132 133 Language getLanguage(in string srcFileName) pure nothrow { 134 switch(srcFileName.extension) with(Language) { 135 case ".d": 136 return D; 137 case ".cpp": 138 case ".CPP": 139 case ".C": 140 case ".cxx": 141 case ".c++": 142 case ".cc": 143 return Cplusplus; 144 case ".c": 145 return C; 146 default: 147 return unknown; 148 } 149 } 150 151 /** 152 Should pull its weight more in the future by automatically figuring out what 153 to do. Right now only works for linking D applications using the configured 154 D compiler 155 */ 156 Target link(in string exeName, in Target[] dependencies, in string flags = "") @safe pure { 157 const command = Command(CommandType.link, assocList([assocEntry("flags", flags.splitter.array)])); 158 return Target(exeName, command, dependencies); 159 }