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 { 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 )() @trusted { 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 71 const srcFiles = modules. 72 map!(a => a.name.removeProjectPath). 73 filter!(srcs.filterFunc). 74 array; 75 76 const dSrcs = srcFiles.filter!(a => a.getLanguage == Language.D).filter!(a => a!= "reggaefile.d").array; 77 auto otherSrcs = srcFiles.filter!(a => a.getLanguage != Language.D && a.getLanguage != Language.unknown); 78 import reggae.rules.d: objectFiles; 79 return objectFiles(dSrcs, flags.value, ["."] ~ includes.value, stringImports.value) ~ 80 otherSrcs.map!(a => objectFile(SourceFile(a), flags, includes)).array; 81 } 82 83 @safe: 84 85 string removeProjectPath(in string path) pure { 86 import std.path: relativePath, absolutePath; 87 //relativePath is @system 88 return () @trusted { return path.absolutePath.relativePath(projectPath.absolutePath); }(); 89 } 90 91 /** 92 An object file, typically from one source file in a certain language 93 (although for D the default is a whole package. The language is determined 94 by the file extension of the file passed in. 95 The $(D projDir) variable is best left alone; right now only the dub targets 96 make use of it (since dub packages are by definition outside of the project 97 source tree). 98 */ 99 Target objectFile(in SourceFile srcFile, 100 in Flags flags = Flags(), 101 in ImportPaths includePaths = ImportPaths(), 102 in StringImportPaths stringImportPaths = StringImportPaths(), 103 in string projDir = "$project") pure { 104 105 const cmd = compileCommand(srcFile.value, flags.value, includePaths.value, stringImportPaths.value, projDir); 106 return Target(srcFile.value.objFileName, cmd, [Target(srcFile.value)]); 107 } 108 109 110 Command compileCommand(in string srcFileName, 111 in string flags = "", 112 in string[] includePaths = [], 113 in string[] stringImportPaths = [], 114 in string projDir = "$project") pure { 115 auto includeParams = includePaths.map!(a => "-I" ~ buildPath(projDir, a)).array; 116 auto flagParams = flags.splitter.array; 117 immutable language = getLanguage(srcFileName); 118 119 auto params = [assocEntry("includes", includeParams), 120 assocEntry("flags", flagParams)]; 121 122 if(language == Language.D) 123 params ~= assocEntry("stringImports", stringImportPaths.map!(a => "-J" ~ buildPath(projDir, a)).array); 124 125 params ~= assocEntry("DEPFILE", ["$out.dep"]); 126 127 return Command(CommandType.compile, assocList(params)); 128 } 129 130 enum Language { 131 C, 132 Cplusplus, 133 D, 134 unknown, 135 } 136 137 Language getLanguage(in string srcFileName) pure nothrow { 138 switch(srcFileName.extension) with(Language) { 139 case ".d": 140 return D; 141 case ".cpp": 142 case ".CPP": 143 case ".C": 144 case ".cxx": 145 case ".c++": 146 case ".cc": 147 return Cplusplus; 148 case ".c": 149 return C; 150 default: 151 return unknown; 152 } 153 } 154 155 /** 156 "Compile-time" link function. 157 Its parameters are compile-time so that it can be aliased and used 158 at global scope in a reggafile. 159 Links an executable from the given dependency targets. The linker used 160 depends on the file extension of the leaf nodes of the passed-in targets. 161 If any D files are found, the linker is the D compiler, and so on with 162 C++ and C. If none of those apply, the D compiler is used. 163 */ 164 Target link(ExeName exeName, alias dependenciesFunc, Flags flags = Flags())() @safe { 165 auto dependencies = dependenciesFunc(); 166 return link(exeName, dependencies, flags); 167 } 168 169 /** 170 Regular run-time link function. 171 Links an executable from the given dependency targets. The linker used 172 depends on the file extension of the leaf nodes of the passed-in targets. 173 If any D files are found, the linker is the D compiler, and so on with 174 C++ and C. If none of those apply, the D compiler is used. 175 */ 176 Target link(in ExeName exeName, in Target[] dependencies, in Flags flags = Flags()) @safe pure { 177 const command = Command(CommandType.link, assocList([assocEntry("flags", flags.value.splitter.array)])); 178 return Target(exeName.value, command, dependencies); 179 }