Skip to content

Instantly share code, notes, and snippets.

@Frityet
Last active April 10, 2026 18:43
Show Gist options
  • Select an option

  • Save Frityet/019adef7a9d772d29780abce0cd4cf49 to your computer and use it in GitHub Desktop.

Select an option

Save Frityet/019adef7a9d772d29780abce0cd4cf49 to your computer and use it in GitHub Desktop.
#ifndef ASYNCRT_BUILD_H
#define ASYNCRT_BUILD_H
#import <ObjFW/ObjFW.h>
#pragma clang assume_nonnull begin
@class Build;
@class BuildBase;
#define BUILD_TARGET [self declaredTargetForSelector: _cmd]
@interface Target : OFObject
@property (readonly, copy, nonatomic) OFString *name;
@property (readonly, copy, nonatomic) OFString *artifactName;
@property (readonly, nonatomic) Target *binary;
@property (readonly, nonatomic) Target *staticLibrary;
@property (readonly, nonatomic) Target *sharedLibrary;
@property (readonly, nonatomic) Target *phony;
@property (readonly, nonatomic) Target *defaultTarget;
@property (readonly, nonatomic) Target *alwaysBuild;
@property (readonly, nonatomic) id dependsOn;
@property (readonly, nonatomic) id publiclyDependsOn;
- (Target *)dependsOn: (id)dependencies;
- (Target *)publiclyDependsOn: (id)dependencies;
- (Target *)outputName: (OFString *)name;
- (Target *)group: (OFString *)name;
- (Target *)pmHeader: (OFString *)path;
- (Target *)workingDirectory: (OFString *)path;
- (Target *)files: (id)paths;
- (Target *)inputs: (id)paths;
- (Target *)includeDirs: (id)paths;
- (Target *)publicIncludeDirs: (id)paths;
- (Target *)defines: (id)defines;
- (Target *)publicDefines: (id)defines;
- (Target *)packages: (id)packages;
- (Target *)publicPackages: (id)packages;
- (Target *)cFlags: (id)flags;
- (Target *)mFlags: (id)flags;
- (Target *)ldFlags: (id)flags;
- (Target *)links: (id)links;
- (Target *)libraryDirs: (id)directories;
- (Target *)fileCFlags: (OFString *)pattern flags: (id)flags;
- (Target *)fileMFlags: (OFString *)pattern flags: (id)flags;
@end
@interface BuildBase : OFObject
@property (copy, nonatomic) OFString *mode;
@property (nonatomic) size_t jobs;
@property (nonatomic) bool verbose;
@property (readonly, copy, nonatomic) OFString *projectRoot;
@property (readonly, copy, nonatomic) OFString *buildRoot;
- (Target *)declaredTargetForSelector: (SEL)selector;
- (nullable Target *)targetNamed: (OFString *)name;
@end
@interface Build : BuildBase
@end
@interface App : OFObject <OFApplicationDelegate>
@end
typedef enum {
TargetKindBinary,
TargetKindStaticLibrary,
TargetKindSharedLibrary,
TargetKindPhony
} TargetKind;
typedef enum {
ActionKindCommand,
ActionKindNoOp
} ActionKind;
@interface BuildBase ()
{
@public
OFString *_projectRoot;
OFString *_mode;
OFString *_platform;
OFString *_architecture;
OFString *_buildBaseRoot;
OFString *_buildRoot;
OFString *_objfwBinDirectory;
OFString *_objfwConfigPath;
OFString *_objfwCompilePath;
OFString *_compiler;
OFString *_archiver;
OFDictionary *_subprocessEnvironment;
OFMutableDictionary *_objfwPackageCPPFlagsByName;
OFMutableDictionary *_objfwPackageStaticLibsByName;
OFMutableDictionary *_objfwPackageLDFlagsByName;
OFArray *_objfwCPPFlags;
OFArray *_objfwObjCFlags;
OFArray *_objfwArcFlags;
OFArray *_objfwStaticLibs;
OFArray *_objfwLDFlags;
OFArray *_modeCFlags;
OFArray *_modeMFlags;
OFArray *_modeLDFlags;
OFMutableArray *_globalIncludeDirs;
OFMutableArray *_globalDefines;
OFMutableArray *_globalCFlags;
OFMutableArray *_globalMFlags;
OFMutableArray *_globalLDFlags;
OFMutableDictionary *_targetsByIdentifier;
OFArray *_knownTargetSelectorNames;
OFArray *_allProjectFileSubpaths;
size_t _jobs;
bool _verbose;
}
@end
@interface Target ()
{
@public
Build *_build;
SEL _selector;
OFString *_name;
OFString *_artifactName;
OFString *_group;
OFString *_pmHeader;
OFString *_workingDirectory;
TargetKind _kind;
bool _configured;
bool _configuring;
bool _defaultTarget;
bool _alwaysBuild;
OFMutableArray *_sourcePatterns;
OFMutableArray *_extraInputs;
OFMutableArray *_includeDirs;
OFMutableArray *_publicIncludeDirs;
OFMutableArray *_defines;
OFMutableArray *_publicDefines;
OFMutableArray *_packages;
OFMutableArray *_publicPackages;
OFMutableArray *_cFlags;
OFMutableArray *_mFlags;
OFMutableArray *_ldFlags;
OFMutableArray *_links;
OFMutableArray *_libraryDirs;
OFMutableArray *_dependencies;
OFMutableArray *_fileCFlags;
OFMutableArray *_fileMFlags;
id _dependsOnProxy;
id _publicDependsOnProxy;
}
- (instancetype)initWithBuild: (Build *)build selector: (SEL)selector;
- (void)addDependencySelector: (SEL)selector publicDependency: (bool)isPublic;
@end
@interface BuildDependencySpec : OFObject
{
@public
SEL _selector;
bool _publicDependency;
}
+ (instancetype)specWithSelector: (SEL)selector publicDependency: (bool)isPublic;
@end
@interface BuildFileFlagRule : OFObject
{
@public
OFString *_pattern;
OFArray *_flags;
}
+ (instancetype)ruleWithPattern: (OFString *)pattern flags: (OFArray *)flags;
@end
@interface BuildDependencyProxy : OFObject
{
Target *_target;
bool _publicDependency;
}
- (instancetype)initWithTarget: (Target *)target
publicDependency: (bool)isPublic;
- (void)captureDependencySelector: (SEL)selector;
@end
@interface BuildAction : OFObject
{
@public
OFString *_key;
OFString *_label;
OFString *_signature;
OFString *_signaturePath;
OFArray *_argv;
OFDictionary *_environment;
OFArray *_inputs;
OFArray *_outputs;
OFMutableArray *_dependencies;
OFMutableArray *_dependents;
OFArray *(^_dynamicInputsProvider)(void);
OFSubprocess *_process;
size_t _remainingDependencies;
ActionKind _kind;
bool _alwaysBuild;
bool _completed;
bool _succeeded;
bool _skipped;
}
+ (instancetype)actionWithKey: (OFString *)key label: (OFString *)label;
@end
@interface BuildEngine : OFObject
{
Build *_build;
OFArray *_requestedTargetNames;
OFString *_runTargetName;
OFArray *_runArguments;
OFMutableDictionary *_finalActionsByTargetName;
OFMutableDictionary *_exportedIncludeDirsByTargetName;
OFMutableDictionary *_exportedDefinesByTargetName;
OFMutableDictionary *_exportedPackagesByTargetName;
OFMutableDictionary *_exportedLinkArtifactsByTargetName;
OFMutableArray *_allActions;
OFMutableArray *_readyActions;
OFSubprocess *_runProcess;
void (^_completion)(int);
size_t _activeActions;
int _exitStatus;
bool _failed;
bool _launchedRunTarget;
}
- (instancetype)initWithBuild: (Build *)build
targetNames: (OFArray *)targetNames
runTargetName: (nullable OFString *)runTargetName
runArguments: (nullable OFArray *)runArguments
completion: (void (^)(int status))completion;
- (void)start;
- (void)pollActionOutput: (BuildAction *)action;
- (void)pollRunProcess;
@end
static OFString *BuildSelectorString(SEL selector)
{
return [OFString stringWithUTF8String: sel_getName(selector)];
}
static bool BuildSelectorHasArguments(SEL selector)
{
return [BuildSelectorString(selector) containsString: @":"];
}
static void BuildAppendFlattenedUniqueObject(OFMutableArray *array, id object)
{
if (object == nil)
return;
if ([object isKindOfClass: [OFArray class]]) {
for (id item in (OFArray *)object)
BuildAppendFlattenedUniqueObject(array, item);
return;
}
if (![array containsObject: object])
[array addObject: object];
}
static OFArray *BuildSplitWhitespace(OFString *string)
{
OFMutableArray *components = [[OFMutableArray alloc] init];
for (OFString *component in [string componentsSeparatedByCharactersInSet:
[OFCharacterSet whitespaceCharacterSet]]) {
if (component.length > 0)
[components addObject: component];
}
return components;
}
static OFString *BuildExpandTilde(OFString *path)
{
OFString *home;
if (![path hasPrefix: @"~/"])
return path;
home = [[OFApplication environment] objectForKey: @"HOME"];
if (home == nil)
return path;
return [home stringByAppendingPathComponent:
[path substringFromIndex: 2]];
}
static OFString *BuildSanitizePathComponent(OFString *component)
{
OFMutableString *sanitized = [component.lowercaseString mutableCopy];
[sanitized replaceOccurrencesOfString: @" " withString: @"-"];
[sanitized replaceOccurrencesOfString: @"/" withString: @"-"];
return sanitized;
}
static OFString *BuildHostArchitecture(void)
{
#if defined(OF_AMD64) || defined(__x86_64__)
return @"x86_64";
#elif defined(OF_X86) || defined(__i386__)
return @"x86";
#elif defined(__aarch64__)
return @"aarch64";
#elif defined(__arm__)
return @"arm";
#elif defined(__riscv) && (__riscv_xlen == 64)
return @"riscv64";
#else
return @"unknown";
#endif
}
static OFString *BuildDynamicLibrarySuffix(void)
{
#ifdef OF_WINDOWS
return @".dll";
#elif defined(__APPLE__)
return @".dylib";
#else
return @".so";
#endif
}
static OFString *BuildExecutableSuffix(void)
{
#ifdef OF_WINDOWS
return @".exe";
#else
return @"";
#endif
}
static bool BuildGlobMatchesCString(const char *pattern, const char *string)
{
if (*pattern == '\0')
return (*string == '\0');
if (*pattern == '*') {
if (*(pattern + 1) == '*') {
pattern += 2;
while (*pattern == '*')
pattern++;
if (*pattern == '\0')
return true;
while (true) {
if (BuildGlobMatchesCString(pattern, string))
return true;
if (*string == '\0')
return false;
string++;
}
}
pattern++;
while (true) {
if (BuildGlobMatchesCString(pattern, string))
return true;
if (*string == '\0' || *string == '/')
return false;
string++;
}
}
if (*pattern == '?') {
if (*string == '\0' || *string == '/')
return false;
return BuildGlobMatchesCString(pattern + 1, string + 1);
}
if (*pattern != *string)
return false;
return BuildGlobMatchesCString(pattern + 1, string + 1);
}
static bool BuildPatternMatchesPath(OFString *pattern, OFString *path)
{
return BuildGlobMatchesCString(pattern.UTF8String, path.UTF8String);
}
static bool BuildPatternHasWildcard(OFString *pattern)
{
return ([pattern containsString: @"*"] || [pattern containsString: @"?"]);
}
static void BuildAppendUniqueString(OFMutableArray *array, OFString *string)
{
if (string == nil)
return;
if (![array containsObject: string])
[array addObject: string];
}
static void BuildAppendUniqueStrings(OFMutableArray *array, OFArray *strings)
{
for (OFString *string in strings)
BuildAppendUniqueString(array, string);
}
static OFString *BuildRelativePath(OFString *root, OFString *path)
{
OFString *standardRoot = root.stringByStandardizingPath;
OFString *standardPath = path.stringByStandardizingPath;
OFString *prefix = [standardRoot stringByAppendingString: @"/"];
if ([standardPath isEqual: standardRoot])
return @".";
if ([standardPath hasPrefix: prefix])
return [standardPath substringFromIndex: prefix.length];
return standardPath;
}
static OFString *BuildProjectAbsolutePath(Build *build, OFString *path)
{
OFString *expanded;
if (path == nil)
return nil;
expanded = BuildExpandTilde(path);
if (expanded.absolutePath)
return expanded.stringByStandardizingPath;
return [[build->_projectRoot stringByAppendingPathComponent: expanded]
stringByStandardizingPath];
}
static OFString *BuildDisplayPath(Build *build, OFString *path)
{
return BuildRelativePath(build->_projectRoot, path);
}
static OFString *BuildSearchPathForExecutable(OFString *name)
{
OFFileManager *fileManager = [OFFileManager defaultManager];
OFString *environmentPath = [[OFApplication environment] objectForKey: @"PATH"];
if (environmentPath == nil)
return nil;
for (OFString *directory in [environmentPath componentsSeparatedByString: @":"]) {
OFString *candidate;
if (directory.length == 0)
continue;
candidate = [directory stringByAppendingPathComponent: name];
if ([fileManager fileExistsAtPath: candidate] &&
![fileManager directoryExistsAtPath: candidate])
return candidate;
}
return nil;
}
static OFString *BuildSearchXmakePackageTool(OFString *name)
{
OFFileManager *fileManager = [OFFileManager defaultManager];
OFString *root = BuildExpandTilde(@"~/.xmake/packages/o/objfw");
OFString *bestCandidate = nil;
OFDate *bestDate = nil;
if (![fileManager directoryExistsAtPath: root])
return nil;
for (OFString *subpath in [fileManager subpathsOfDirectoryAtPath: root]) {
OFString *candidate;
OFDate *candidateDate;
if ([subpath hasSuffix:
[@"/bin/" stringByAppendingString: name]])
candidate = (subpath.absolutePath
? subpath.stringByStandardizingPath
: [root stringByAppendingPathComponent: subpath]);
else
continue;
candidateDate = [fileManager attributesOfItemAtPath: candidate]
.fileModificationDate;
if (bestDate == nil ||
[candidateDate compare: bestDate] == OFOrderedDescending) {
bestCandidate = candidate;
bestDate = candidateDate;
}
}
return bestCandidate;
}
static OFString *BuildFindExecutable(OFString *environmentVariable,
OFString *name)
{
OFFileManager *fileManager = [OFFileManager defaultManager];
OFString *candidate = [[OFApplication environment]
objectForKey: environmentVariable];
if (candidate != nil) {
candidate = BuildExpandTilde(candidate);
if ([fileManager fileExistsAtPath: candidate] &&
![fileManager directoryExistsAtPath: candidate])
return candidate;
}
candidate = BuildSearchPathForExecutable(name);
if (candidate != nil)
return candidate;
return BuildSearchXmakePackageTool(name);
}
static OFDictionary *BuildEnvironmentByPrependingPathComponent(
OFString *directory)
{
OFMutableDictionary *environment = [[[OFApplication environment] mutableCopy]
?: [[OFMutableDictionary alloc] init] mutableCopy];
OFString *existingPath = [environment objectForKey: @"PATH"];
OFString *prefixedPath;
if (directory == nil || directory.length == 0)
return environment;
if ([existingPath isEqual: directory] ||
[existingPath hasPrefix:
[directory stringByAppendingString: @":"]])
return environment;
prefixedPath = (existingPath.length > 0
? [OFString stringWithFormat: @"%@:%@", directory, existingPath]
: directory);
[environment setObject: prefixedPath forKey: @"PATH"];
return environment;
}
static OFString *BuildRunTool(OFString *program, OFArray *arguments,
OFDictionary *environment)
{
OFSubprocess *process;
OFString *output;
process = [OFSubprocess subprocessWithProgram: program
programName: program
arguments: arguments
environment: environment];
[process closeForWriting];
output = [[process readString] stringByDeletingEnclosingWhitespaces];
if ([process waitForTermination] != 0)
@throw [OFInitializationFailedException exceptionWithClass: Build.class];
return output;
}
static OFArray *BuildObjFWPackageConfigFlags(Build *build, OFString *package,
OFArray *configArguments, OFMutableDictionary *cache)
{
OFArray *cached = [cache objectForKey: package];
OFMutableArray *arguments;
if (cached != nil)
return cached;
arguments = [[OFMutableArray alloc] init];
[arguments addObject: @"--package"];
[arguments addObject: package];
[arguments addObjectsFromArray: configArguments];
cached = [BuildSplitWhitespace(BuildRunTool(build->_objfwConfigPath,
arguments, build->_subprocessEnvironment)) copy];
[cache setObject: cached forKey: package];
return cached;
}
static OFArray *BuildResolvePackageCPPFlags(Build *build, OFArray *packages)
{
OFMutableArray *flags = [[OFMutableArray alloc] init];
for (OFString *package in packages)
BuildAppendUniqueStrings(flags, BuildObjFWPackageConfigFlags(build,
package, [OFArray arrayWithObject: @"--cppflags"],
build->_objfwPackageCPPFlagsByName));
return flags;
}
static OFArray *BuildResolvePackageStaticLibs(Build *build, OFArray *packages)
{
OFMutableArray *flags = [[OFMutableArray alloc] init];
for (OFString *package in packages)
BuildAppendUniqueStrings(flags, BuildObjFWPackageConfigFlags(build,
package, [OFArray arrayWithObject: @"--libs"],
build->_objfwPackageStaticLibsByName));
return flags;
}
static OFArray *BuildResolvePackageLDFlags(Build *build, OFArray *packages)
{
OFMutableArray *flags = [[OFMutableArray alloc] init];
for (OFString *package in packages)
BuildAppendUniqueStrings(flags, BuildObjFWPackageConfigFlags(build,
package, [OFArray arrayWithObjects: @"--ldflags", @"--rpath", nil],
build->_objfwPackageLDFlagsByName));
return flags;
}
static void BuildRefreshMode(Build *build)
{
OFMutableArray *modeCFlags = [[OFMutableArray alloc] init];
OFMutableArray *modeMFlags = [[OFMutableArray alloc] init];
OFMutableArray *modeLDFlags = [[OFMutableArray alloc] init];
OFString *normalizedMode = build->_mode.lowercaseString;
build->_buildRoot = [[[[build->_buildBaseRoot
stringByAppendingPathComponent: build->_platform]
stringByAppendingPathComponent: build->_architecture]
stringByAppendingPathComponent: normalizedMode] stringByStandardizingPath];
if ([normalizedMode isEqual: @"release"]) {
[modeCFlags addObject: @"-O3"];
[modeCFlags addObject: @"-DNDEBUG"];
[modeMFlags addObjectsFromArray: modeCFlags];
} else if ([normalizedMode isEqual: @"asan"]) {
[modeCFlags addObjectsFromArray: [OFArray arrayWithObjects:
@"-O1", @"-g", @"-fsanitize=address", @"-fsanitize=undefined",
nil]];
[modeMFlags addObjectsFromArray: modeCFlags];
[modeLDFlags addObjectsFromArray: [OFArray arrayWithObjects:
@"-fsanitize=address", @"-fsanitize=undefined", nil]];
} else if ([normalizedMode isEqual: @"tsan"]) {
[modeCFlags addObjectsFromArray: [OFArray arrayWithObjects:
@"-O1", @"-g", @"-fsanitize=thread", nil]];
[modeMFlags addObjectsFromArray: modeCFlags];
[modeLDFlags addObject: @"-fsanitize=thread"];
} else {
[modeCFlags addObjectsFromArray: [OFArray arrayWithObjects:
@"-O0", @"-g", nil]];
[modeMFlags addObjectsFromArray: modeCFlags];
}
build->_modeCFlags = [modeCFlags copy];
build->_modeMFlags = [modeMFlags copy];
build->_modeLDFlags = [modeLDFlags copy];
}
static void BuildConfigureToolchain(Build *build)
{
OFFileManager *fileManager = [OFFileManager defaultManager];
OFMutableArray *baseMFlags;
OFString *archiver;
OFString *siblingConfigPath;
build->_objfwCompilePath = [BuildFindExecutable(@"OBJFW_COMPILE",
@"objfw-compile") copy];
if (build->_objfwCompilePath == nil) {
[OFStdErr writeLine:
@"Unable to find objfw-compile. Set OBJFW_COMPILE or install ObjFW's tools."];
@throw [OFInitializationFailedException exceptionWithClass: Build.class];
}
build->_objfwBinDirectory =
[[build->_objfwCompilePath stringByDeletingLastPathComponent] copy];
build->_subprocessEnvironment = [BuildEnvironmentByPrependingPathComponent(
build->_objfwBinDirectory) copy];
siblingConfigPath = [build->_objfwBinDirectory
stringByAppendingPathComponent: @"objfw-config"];
if ([fileManager fileExistsAtPath: siblingConfigPath] &&
![fileManager directoryExistsAtPath: siblingConfigPath])
build->_objfwConfigPath = [siblingConfigPath copy];
else
build->_objfwConfigPath = [BuildFindExecutable(@"OBJFW_CONFIG",
@"objfw-config") copy];
if (build->_objfwConfigPath == nil) {
[OFStdErr writeLine:
@"Unable to find objfw-config. Set OBJFW_CONFIG or install ObjFW's tools."];
@throw [OFInitializationFailedException exceptionWithClass: Build.class];
}
build->_compiler = [[BuildRunTool(build->_objfwConfigPath,
[OFArray arrayWithObject: @"--objc"],
build->_subprocessEnvironment)
stringByDeletingEnclosingWhitespaces] copy];
archiver = BuildFindExecutable(@"AR", @"llvm-ar");
if (archiver == nil)
archiver = BuildFindExecutable(@"AR", @"ar");
if (archiver == nil)
archiver = @"ar";
build->_archiver = [archiver copy];
build->_objfwCPPFlags = [BuildSplitWhitespace(BuildRunTool(
build->_objfwConfigPath, [OFArray arrayWithObject: @"--cppflags"],
build->_subprocessEnvironment)) copy];
build->_objfwObjCFlags = [BuildSplitWhitespace(BuildRunTool(
build->_objfwConfigPath, [OFArray arrayWithObject: @"--objcflags"],
build->_subprocessEnvironment)) copy];
build->_objfwArcFlags = [BuildSplitWhitespace(BuildRunTool(
build->_objfwConfigPath, [OFArray arrayWithObject: @"--arc"],
build->_subprocessEnvironment)) copy];
build->_objfwStaticLibs = [BuildSplitWhitespace(BuildRunTool(
build->_objfwConfigPath, [OFArray arrayWithObject: @"--static-libs"],
build->_subprocessEnvironment)) copy];
build->_objfwLDFlags = [BuildSplitWhitespace(BuildRunTool(
build->_objfwConfigPath,
[OFArray arrayWithObjects: @"--ldflags", @"--rpath", nil],
build->_subprocessEnvironment)) copy];
baseMFlags = [[OFMutableArray alloc] init];
[baseMFlags addObjectsFromArray: [OFArray arrayWithObjects:
@"-std=gnu23",
@"-Werror",
@"-Wall",
@"-Wextra",
@"-fms-extensions",
@"-Wno-microsoft",
@"-Wno-unused-function",
@"-Wanon-enum-enum-conversion",
@"-Wassign-enum",
@"-Wenum-conversion",
@"-Wenum-enum-conversion",
@"-Wnull-dereference",
@"-Wnull-conversion",
@"-Wnullability-completeness",
@"-Wnullable-to-nonnull-conversion",
@"-Wno-auto-var-id",
@"-Wno-missing-braces",
nil]];
build->_globalMFlags = baseMFlags;
build->_globalCFlags = [[OFMutableArray alloc] init];
build->_globalIncludeDirs = [[OFMutableArray alloc] init];
build->_globalDefines = [[OFMutableArray alloc] init];
build->_globalLDFlags = [[OFMutableArray alloc] init];
[build->_globalIncludeDirs addObject: @"src"];
if ([build->_platform isEqual: @"linux"]) {
[build->_globalMFlags addObject: @"-fno-omit-frame-pointer"];
[build->_globalLDFlags addObject: @"-rdynamic"];
}
BuildRefreshMode(build);
}
static bool BuildSelectorNameIsReserved(OFString *name)
{
return ([name isEqual: @"init"] ||
[name isEqual: @"mode"] ||
[name isEqual: @"jobs"] ||
[name isEqual: @"verbose"] ||
[name isEqual: @"projectRoot"] ||
[name isEqual: @"buildRoot"]);
}
static OFArray *BuildKnownTargetSelectorNames(Build *build)
{
Method *methodList;
unsigned int count = 0;
OFMutableArray *names;
if (build->_knownTargetSelectorNames != nil)
return build->_knownTargetSelectorNames;
methodList = class_copyMethodList(build.class, &count);
names = [[OFMutableArray alloc] init];
@try {
for (unsigned int index = 0; index < count; index++) {
SEL selector = method_getName(methodList[index]);
OFString *name = BuildSelectorString(selector);
if (BuildSelectorHasArguments(selector))
continue;
if (BuildSelectorNameIsReserved(name))
continue;
[names addObject: name];
}
} @finally {
OFFreeMemory(methodList);
}
[names sort];
build->_knownTargetSelectorNames = [names copy];
return build->_knownTargetSelectorNames;
}
static bool BuildIsKnownTargetSelector(Build *build, SEL selector)
{
return [BuildKnownTargetSelectorNames(build)
containsObject: BuildSelectorString(selector)];
}
static void BuildEnsureProjectFileCache(Build *build)
{
OFFileManager *fileManager = [OFFileManager defaultManager];
OFMutableArray *files;
if (build->_allProjectFileSubpaths != nil)
return;
files = [[OFMutableArray alloc] init];
for (OFString *subpath in [fileManager
subpathsOfDirectoryAtPath: build->_projectRoot]) {
OFString *absolutePath;
OFString *relativePath;
absolutePath = (subpath.absolutePath
? subpath.stringByStandardizingPath
: [[build->_projectRoot stringByAppendingPathComponent: subpath]
stringByStandardizingPath]);
relativePath = BuildRelativePath(build->_projectRoot, absolutePath);
if (![fileManager directoryExistsAtPath: absolutePath])
[files addObject: relativePath];
}
[files sort];
build->_allProjectFileSubpaths = [files copy];
}
static OFString *BuildNormalizeProjectPattern(Build *build, OFString *pattern)
{
OFString *expanded = BuildExpandTilde(pattern);
OFString *prefix;
if (!expanded.absolutePath)
return expanded.stringByStandardizingPath;
prefix = [build->_projectRoot stringByAppendingString: @"/"];
if ([expanded hasPrefix: prefix])
return [expanded substringFromIndex: prefix.length];
return expanded.stringByStandardizingPath;
}
static OFArray *BuildResolvedSourcePaths(Build *build, Target *target)
{
OFFileManager *fileManager = [OFFileManager defaultManager];
OFMutableArray *resolved = [[OFMutableArray alloc] init];
OFMutableDictionary *seen = [[OFMutableDictionary alloc] init];
BuildEnsureProjectFileCache(build);
for (OFString *patternSpec in target->_sourcePatterns) {
OFArray *components = [patternSpec componentsSeparatedByString: @"|"];
OFString *includePattern;
OFMutableArray *excludePatterns = [[OFMutableArray alloc] init];
if (components.count == 0)
continue;
includePattern = BuildNormalizeProjectPattern(build,
[components objectAtIndex: 0]);
for (size_t index = 1; index < components.count; index++) {
[excludePatterns addObject: BuildNormalizeProjectPattern(build,
[components objectAtIndex: index])];
}
if (!BuildPatternHasWildcard(includePattern)) {
OFString *absolutePath =
[build->_projectRoot stringByAppendingPathComponent: includePattern];
if ([fileManager fileExistsAtPath: absolutePath] &&
![fileManager directoryExistsAtPath: absolutePath] &&
[seen objectForKey: includePattern] == nil) {
[seen setObject: @YES forKey: includePattern];
[resolved addObject: includePattern];
}
continue;
}
for (OFString *subpath in build->_allProjectFileSubpaths) {
bool excluded = false;
if (!BuildPatternMatchesPath(includePattern, subpath))
continue;
for (OFString *excludePattern in excludePatterns) {
if (BuildPatternMatchesPath(excludePattern, subpath)) {
excluded = true;
break;
}
}
if (!excluded && [seen objectForKey: subpath] == nil) {
[seen setObject: @YES forKey: subpath];
[resolved addObject: subpath];
}
}
}
[resolved sort];
return resolved;
}
static OFArray *BuildParseDepfile(OFString *path)
{
OFFileManager *fileManager = [OFFileManager defaultManager];
OFMutableString *contents;
OFMutableArray *dependencies;
OFRange colon;
if (![fileManager fileExistsAtPath: path])
return nil;
contents = [[OFString stringWithContentsOfFile: path] mutableCopy];
[contents replaceOccurrencesOfString: @"\\\n" withString: @""];
colon = [contents rangeOfString: @":"];
if (colon.location == OFNotFound)
return nil;
dependencies = [[OFMutableArray alloc] init];
for (OFString *token in [[contents substringFromIndex: colon.location + 1]
componentsSeparatedByCharactersInSet:
[OFCharacterSet whitespaceCharacterSet]]) {
if (token.length > 0)
[dependencies addObject: token];
}
return dependencies;
}
static OFString *BuildActionSignature(BuildAction *action)
{
OFMutableArray *parts = [[OFMutableArray alloc] init];
if (action->_argv != nil)
[parts addObject: [action->_argv componentsJoinedByString: @"\n"]];
if (action->_inputs != nil)
[parts addObject: [action->_inputs componentsJoinedByString: @"\n"]];
if (action->_outputs != nil)
[parts addObject: [action->_outputs componentsJoinedByString: @"\n"]];
return [parts componentsJoinedByString: @"\n--\n"];
}
static bool BuildActionIsUpToDate(BuildAction *action)
{
OFFileManager *fileManager = [OFFileManager defaultManager];
OFDate *oldestOutput = nil;
OFDate *newestInput = nil;
OFString *storedSignature;
OFArray *dynamicInputs;
if (action->_alwaysBuild || action->_kind != ActionKindCommand)
return false;
if (action->_outputs.count == 0 || action->_signaturePath == nil)
return false;
for (OFString *output in action->_outputs) {
OFDate *modificationDate;
if (![fileManager fileExistsAtPath: output])
return false;
modificationDate = [fileManager attributesOfItemAtPath: output]
.fileModificationDate;
if (oldestOutput == nil ||
[modificationDate compare: oldestOutput] == OFOrderedAscending)
oldestOutput = modificationDate;
}
if (![fileManager fileExistsAtPath: action->_signaturePath])
return false;
storedSignature = [[OFString stringWithContentsOfFile: action->_signaturePath]
stringByDeletingEnclosingWhitespaces];
if (![storedSignature isEqual: action->_signature])
return false;
dynamicInputs = (action->_dynamicInputsProvider != nil
? action->_dynamicInputsProvider()
: nil);
for (OFString *input in action->_inputs) {
OFDate *modificationDate;
if (![fileManager fileExistsAtPath: input])
return false;
modificationDate = [fileManager attributesOfItemAtPath: input]
.fileModificationDate;
if (newestInput == nil ||
[modificationDate compare: newestInput] == OFOrderedDescending)
newestInput = modificationDate;
}
for (OFString *input in dynamicInputs) {
OFDate *modificationDate;
if (![fileManager fileExistsAtPath: input])
return false;
modificationDate = [fileManager attributesOfItemAtPath: input]
.fileModificationDate;
if (newestInput == nil ||
[modificationDate compare: newestInput] == OFOrderedDescending)
newestInput = modificationDate;
}
if (newestInput == nil)
return false;
return ([oldestOutput compare: newestInput] != OFOrderedAscending);
}
static void BuildAddActionDependency(BuildAction *action, BuildAction *dependency)
{
if (dependency == nil)
return;
if ([action->_dependencies containsObject: dependency])
return;
[action->_dependencies addObject: dependency];
[dependency->_dependents addObject: action];
}
static OFArray *BuildResolvePathList(Build *build, OFArray *paths)
{
OFMutableArray *resolved = [[OFMutableArray alloc] init];
for (OFString *path in paths)
[resolved addObject: BuildProjectAbsolutePath(build, path)];
return resolved;
}
static OFArray *BuildResolveDefineFlags(OFArray *defines)
{
OFMutableArray *flags = [[OFMutableArray alloc] init];
for (OFString *define in defines) {
if ([define hasPrefix: @"-D"])
[flags addObject: define];
else
[flags addObject: [@"-D" stringByAppendingString: define]];
}
return flags;
}
static OFArray *BuildResolveIncludeFlags(Build *build, OFArray *paths)
{
OFMutableArray *flags = [[OFMutableArray alloc] init];
for (OFString *path in paths) {
[flags addObject: @"-I"];
[flags addObject: BuildProjectAbsolutePath(build, path)];
}
return flags;
}
static OFArray *BuildResolveLibraryDirectoryFlags(Build *build, OFArray *paths)
{
OFMutableArray *flags = [[OFMutableArray alloc] init];
for (OFString *path in paths) {
[flags addObject: @"-L"];
[flags addObject: BuildProjectAbsolutePath(build, path)];
}
return flags;
}
static OFArray *BuildResolveLinkFlags(OFArray *links)
{
OFMutableArray *flags = [[OFMutableArray alloc] init];
for (OFString *link in links) {
if ([link hasPrefix: @"-"] || link.absolutePath ||
[link containsString: @"/"]) {
[flags addObject: link];
} else {
[flags addObject: [@"-l" stringByAppendingString: link]];
}
}
return flags;
}
static OFArray *BuildCollectMatchingFlags(OFArray *rules, OFString *sourcePath)
{
OFMutableArray *flags = [[OFMutableArray alloc] init];
for (BuildFileFlagRule *rule in rules) {
if (BuildPatternMatchesPath(rule->_pattern, sourcePath))
BuildAppendUniqueStrings(flags, rule->_flags);
}
return flags;
}
static void BuildAppendDependenciesFromObject(Target *target, id object,
bool isPublic)
{
if (object == nil)
return;
if ([object isKindOfClass: [OFArray class]]) {
for (id item in (OFArray *)object)
BuildAppendDependenciesFromObject(target, item, isPublic);
return;
}
if ([object isKindOfClass: [Target class]]) {
[target addDependencySelector: ((Target *)object)->_selector
publicDependency: isPublic];
return;
}
if ([object isKindOfClass: [OFString class]]) {
[target addDependencySelector:
sel_registerName(((OFString *)object).UTF8String)
publicDependency: isPublic];
return;
}
}
static id BuildDependencyProxyIMP(id self, SEL _cmd)
{
[(BuildDependencyProxy *)self captureDependencySelector: _cmd];
return self;
}
@implementation BuildDependencySpec
+ (instancetype)specWithSelector: (SEL)selector publicDependency: (bool)isPublic
{
BuildDependencySpec *spec = [[self alloc] init];
spec->_selector = selector;
spec->_publicDependency = isPublic;
return spec;
}
@end
@implementation BuildFileFlagRule
+ (instancetype)ruleWithPattern: (OFString *)pattern flags: (OFArray *)flags
{
BuildFileFlagRule *rule = [[self alloc] init];
rule->_pattern = [pattern copy];
rule->_flags = [flags copy];
return rule;
}
@end
@implementation BuildDependencyProxy
+ (bool)resolveInstanceMethod: (SEL)selector
{
if ([Target instancesRespondToSelector: selector])
return [super resolveInstanceMethod: selector];
if (!BuildSelectorHasArguments(selector)) {
class_addMethod(self, selector, (IMP)BuildDependencyProxyIMP, "@@:");
return true;
}
return [super resolveInstanceMethod: selector];
}
- (instancetype)initWithTarget: (Target *)target
publicDependency: (bool)isPublic
{
self = [super init];
_target = target;
_publicDependency = isPublic;
return self;
}
- (void)captureDependencySelector: (SEL)selector
{
[_target addDependencySelector: selector publicDependency: _publicDependency];
}
- (nullable id)forwardingTargetForSelector: (SEL)selector
{
if ([Target instancesRespondToSelector: selector])
return _target;
return [super forwardingTargetForSelector: selector];
}
@end
@implementation BuildAction
+ (instancetype)actionWithKey: (OFString *)key label: (OFString *)label
{
BuildAction *action = [[self alloc] init];
action->_key = [key copy];
action->_label = [label copy];
action->_dependencies = [[OFMutableArray alloc] init];
action->_dependents = [[OFMutableArray alloc] init];
return action;
}
@end
@implementation Target
- (instancetype)initWithBuild: (Build *)build selector: (SEL)selector
{
self = [super init];
_build = build;
_selector = selector;
_name = [BuildSelectorString(selector) copy];
_kind = TargetKindBinary;
_sourcePatterns = [[OFMutableArray alloc] init];
_extraInputs = [[OFMutableArray alloc] init];
_includeDirs = [[OFMutableArray alloc] init];
_publicIncludeDirs = [[OFMutableArray alloc] init];
_defines = [[OFMutableArray alloc] init];
_publicDefines = [[OFMutableArray alloc] init];
_packages = [[OFMutableArray alloc] init];
_publicPackages = [[OFMutableArray alloc] init];
_cFlags = [[OFMutableArray alloc] init];
_mFlags = [[OFMutableArray alloc] init];
_ldFlags = [[OFMutableArray alloc] init];
_links = [[OFMutableArray alloc] init];
_libraryDirs = [[OFMutableArray alloc] init];
_dependencies = [[OFMutableArray alloc] init];
_fileCFlags = [[OFMutableArray alloc] init];
_fileMFlags = [[OFMutableArray alloc] init];
return self;
}
- (OFString *)name
{
return _name;
}
- (OFString *)artifactName
{
return (_artifactName != nil ? _artifactName : _name);
}
- (Target *)binary
{
_kind = TargetKindBinary;
return self;
}
- (Target *)staticLibrary
{
_kind = TargetKindStaticLibrary;
return self;
}
- (Target *)sharedLibrary
{
_kind = TargetKindSharedLibrary;
return self;
}
- (Target *)phony
{
_kind = TargetKindPhony;
return self;
}
- (Target *)defaultTarget
{
_defaultTarget = true;
return self;
}
- (Target *)alwaysBuild
{
_alwaysBuild = true;
return self;
}
- (id)dependsOn
{
if (_dependsOnProxy == nil)
_dependsOnProxy = [[BuildDependencyProxy alloc]
initWithTarget: self
publicDependency: false];
return _dependsOnProxy;
}
- (id)publiclyDependsOn
{
if (_publicDependsOnProxy == nil)
_publicDependsOnProxy = [[BuildDependencyProxy alloc]
initWithTarget: self
publicDependency: true];
return _publicDependsOnProxy;
}
- (Target *)dependsOn: (id)dependencies
{
BuildAppendDependenciesFromObject(self, dependencies, false);
return self;
}
- (Target *)publiclyDependsOn: (id)dependencies
{
BuildAppendDependenciesFromObject(self, dependencies, true);
return self;
}
- (void)addDependencySelector: (SEL)selector publicDependency: (bool)isPublic
{
for (BuildDependencySpec *dependency in _dependencies) {
if (dependency->_selector == selector &&
dependency->_publicDependency == isPublic)
return;
}
[_dependencies addObject:
[BuildDependencySpec specWithSelector: selector
publicDependency: isPublic]];
}
- (Target *)outputName: (OFString *)name
{
_artifactName = [name copy];
return self;
}
- (Target *)group: (OFString *)name
{
_group = [name copy];
return self;
}
- (Target *)pmHeader: (OFString *)path
{
_pmHeader = [path copy];
return self;
}
- (Target *)workingDirectory: (OFString *)path
{
_workingDirectory = [path copy];
return self;
}
- (Target *)files: (id)paths
{
BuildAppendFlattenedUniqueObject(_sourcePatterns, paths);
return self;
}
- (Target *)inputs: (id)paths
{
BuildAppendFlattenedUniqueObject(_extraInputs, paths);
return self;
}
- (Target *)includeDirs: (id)paths
{
BuildAppendFlattenedUniqueObject(_includeDirs, paths);
return self;
}
- (Target *)publicIncludeDirs: (id)paths
{
BuildAppendFlattenedUniqueObject(_publicIncludeDirs, paths);
return self;
}
- (Target *)defines: (id)defines
{
BuildAppendFlattenedUniqueObject(_defines, defines);
return self;
}
- (Target *)publicDefines: (id)defines
{
BuildAppendFlattenedUniqueObject(_publicDefines, defines);
return self;
}
- (Target *)packages: (id)packages
{
BuildAppendFlattenedUniqueObject(_packages, packages);
return self;
}
- (Target *)publicPackages: (id)packages
{
BuildAppendFlattenedUniqueObject(_publicPackages, packages);
return self;
}
- (Target *)cFlags: (id)flags
{
BuildAppendFlattenedUniqueObject(_cFlags, flags);
return self;
}
- (Target *)mFlags: (id)flags
{
BuildAppendFlattenedUniqueObject(_mFlags, flags);
return self;
}
- (Target *)ldFlags: (id)flags
{
BuildAppendFlattenedUniqueObject(_ldFlags, flags);
return self;
}
- (Target *)links: (id)links
{
BuildAppendFlattenedUniqueObject(_links, links);
return self;
}
- (Target *)libraryDirs: (id)directories
{
BuildAppendFlattenedUniqueObject(_libraryDirs, directories);
return self;
}
- (Target *)fileCFlags: (OFString *)pattern flags: (id)flags
{
OFMutableArray *flattenedFlags = [[OFMutableArray alloc] init];
BuildAppendFlattenedUniqueObject(flattenedFlags, flags);
[_fileCFlags addObject: [BuildFileFlagRule ruleWithPattern: pattern
flags: flattenedFlags]];
return self;
}
- (Target *)fileMFlags: (OFString *)pattern flags: (id)flags
{
OFMutableArray *flattenedFlags = [[OFMutableArray alloc] init];
BuildAppendFlattenedUniqueObject(flattenedFlags, flags);
[_fileMFlags addObject: [BuildFileFlagRule ruleWithPattern: pattern
flags: flattenedFlags]];
return self;
}
- (OFString *)description
{
return [OFString stringWithFormat:
@"<Target %@ kind=%d artifact=%@>", _name, _kind, self.artifactName];
}
@end
static Target *BuildLoadTarget(Build *build, SEL selector)
{
Target *target;
if (!BuildIsKnownTargetSelector(build, selector))
return nil;
target = [build declaredTargetForSelector: selector];
if (target->_configured)
return target;
if (target->_configuring)
return target;
target->_configuring = true;
@try {
Target *(*method)(id, SEL) =
(Target *(*)(id, SEL))(void *)[build methodForSelector: selector];
id returnedTarget = method(build, selector);
if (returnedTarget != nil && returnedTarget != target &&
[returnedTarget isKindOfClass: [Target class]])
target = returnedTarget;
target->_configured = true;
target->_configuring = false;
return target;
} @catch (id exception) {
target->_configuring = false;
@throw exception;
}
}
static OFString *BuildArtifactPath(Build *build, Target *target)
{
OFString *name = target.artifactName;
switch (target->_kind) {
case TargetKindBinary:
return [build->_buildRoot stringByAppendingPathComponent:
[name stringByAppendingString: BuildExecutableSuffix()]];
case TargetKindStaticLibrary:
return [build->_buildRoot stringByAppendingPathComponent:
[[[@"lib" stringByAppendingString: name]
stringByAppendingPathExtension: @"a"] stringByStandardizingPath]];
case TargetKindSharedLibrary:
return [build->_buildRoot stringByAppendingPathComponent:
[[@"lib" stringByAppendingString: name]
stringByAppendingString: BuildDynamicLibrarySuffix()]];
case TargetKindPhony:
default:
return nil;
}
}
static OFString *BuildObjectPath(Build *build, Target *target, OFString *sourcePath)
{
OFString *objectRoot = [[[[build->_buildBaseRoot
stringByAppendingPathComponent: @".objs"]
stringByAppendingPathComponent: target->_name]
stringByAppendingPathComponent: build->_platform]
stringByAppendingPathComponent: build->_architecture];
OFString *modeRoot = [objectRoot stringByAppendingPathComponent: build->_mode];
return [[modeRoot stringByAppendingPathComponent:
[sourcePath stringByAppendingString: @".o"]] stringByStandardizingPath];
}
@implementation BuildBase
- (instancetype)init
{
self = [super init];
_projectRoot = [[[OFFileManager defaultManager] currentDirectoryPath]
stringByStandardizingPath];
_platform = [BuildSanitizePathComponent(
[OFSystemInfo operatingSystemName] ?: @"unknown") copy];
_architecture = [BuildHostArchitecture() copy];
_buildBaseRoot = [[_projectRoot stringByAppendingPathComponent: @"build"] copy];
_targetsByIdentifier = [[OFMutableDictionary alloc] init];
_objfwPackageCPPFlagsByName = [[OFMutableDictionary alloc] init];
_objfwPackageStaticLibsByName = [[OFMutableDictionary alloc] init];
_objfwPackageLDFlagsByName = [[OFMutableDictionary alloc] init];
_mode = @"debug";
_jobs = ([OFSystemInfo numberOfCPUs] > 0 ? [OFSystemInfo numberOfCPUs] : 1);
_verbose = false;
BuildConfigureToolchain((Build *)self);
return self;
}
- (OFString *)projectRoot
{
return _projectRoot;
}
- (OFString *)buildRoot
{
return _buildRoot;
}
- (OFString *)mode
{
return _mode;
}
- (void)setMode: (OFString *)mode
{
_mode = [mode copy];
BuildRefreshMode((Build *)self);
}
- (size_t)jobs
{
return _jobs;
}
- (void)setJobs: (size_t)jobs
{
_jobs = (jobs > 0 ? jobs : 1);
}
- (bool)verbose
{
return _verbose;
}
- (void)setVerbose: (bool)verbose
{
_verbose = verbose;
}
- (Target *)declaredTargetForSelector: (SEL)selector
{
OFString *identifier = BuildSelectorString(selector);
Target *target = [_targetsByIdentifier objectForKey: identifier];
if (target == nil) {
target = [[Target alloc] initWithBuild: (Build *)self
selector: selector];
[_targetsByIdentifier setObject: target forKey: identifier];
}
return target;
}
- (nullable Target *)targetNamed: (OFString *)name
{
SEL selector;
Target *target;
target = [_targetsByIdentifier objectForKey: name];
if (target != nil)
return target;
selector = sel_registerName(name.UTF8String);
if (BuildIsKnownTargetSelector((Build *)self, selector))
return BuildLoadTarget((Build *)self, selector);
for (OFString *selectorName in BuildKnownTargetSelectorNames((Build *)self))
BuildLoadTarget((Build *)self, sel_registerName(selectorName.UTF8String));
for (OFString *selectorName in _targetsByIdentifier) {
Target *candidate = [_targetsByIdentifier objectForKey: selectorName];
if ([candidate.artifactName isEqual: name] ||
[candidate.name isEqual: name])
return candidate;
}
return nil;
}
@end
@implementation BuildEngine
- (instancetype)initWithBuild: (Build *)build
targetNames: (OFArray *)targetNames
runTargetName: (nullable OFString *)runTargetName
runArguments: (nullable OFArray *)runArguments
completion: (void (^)(int status))completion
{
self = [super init];
_build = build;
_requestedTargetNames = [targetNames copy];
_runTargetName = [runTargetName copy];
_runArguments = [runArguments copy];
_completion = [completion copy];
_finalActionsByTargetName = [[OFMutableDictionary alloc] init];
_exportedIncludeDirsByTargetName = [[OFMutableDictionary alloc] init];
_exportedDefinesByTargetName = [[OFMutableDictionary alloc] init];
_exportedPackagesByTargetName = [[OFMutableDictionary alloc] init];
_exportedLinkArtifactsByTargetName = [[OFMutableDictionary alloc] init];
_allActions = [[OFMutableArray alloc] init];
_readyActions = [[OFMutableArray alloc] init];
_exitStatus = 0;
return self;
}
- (OFArray *)exportedIncludeDirsForTarget: (Target *)target
{
OFArray *cached = [_exportedIncludeDirsByTargetName objectForKey: target->_name];
OFMutableArray *includeDirs;
if (cached != nil)
return cached;
includeDirs = [[OFMutableArray alloc] init];
BuildAppendUniqueStrings(includeDirs,
BuildResolvePathList(_build, target->_publicIncludeDirs));
BuildAppendUniqueStrings(includeDirs,
BuildResolvePathList(_build, target->_includeDirs));
for (BuildDependencySpec *dependency in target->_dependencies) {
Target *dependencyTarget;
if (!dependency->_publicDependency)
continue;
dependencyTarget = BuildLoadTarget(_build, dependency->_selector);
BuildAppendUniqueStrings(includeDirs,
[self exportedIncludeDirsForTarget: dependencyTarget]);
}
cached = [includeDirs copy];
[_exportedIncludeDirsByTargetName setObject: cached forKey: target->_name];
return cached;
}
- (OFArray *)exportedDefinesForTarget: (Target *)target
{
OFArray *cached = [_exportedDefinesByTargetName objectForKey: target->_name];
OFMutableArray *defines;
if (cached != nil)
return cached;
defines = [[OFMutableArray alloc] init];
BuildAppendUniqueStrings(defines, target->_publicDefines);
BuildAppendUniqueStrings(defines, target->_defines);
for (BuildDependencySpec *dependency in target->_dependencies) {
Target *dependencyTarget;
if (!dependency->_publicDependency)
continue;
dependencyTarget = BuildLoadTarget(_build, dependency->_selector);
BuildAppendUniqueStrings(defines,
[self exportedDefinesForTarget: dependencyTarget]);
}
cached = [defines copy];
[_exportedDefinesByTargetName setObject: cached forKey: target->_name];
return cached;
}
- (OFArray *)exportedPackagesForTarget: (Target *)target
{
OFArray *cached = [_exportedPackagesByTargetName objectForKey: target->_name];
OFMutableArray *packages;
if (cached != nil)
return cached;
packages = [[OFMutableArray alloc] init];
BuildAppendUniqueStrings(packages, target->_publicPackages);
for (BuildDependencySpec *dependency in target->_dependencies) {
Target *dependencyTarget;
if (!dependency->_publicDependency)
continue;
dependencyTarget = BuildLoadTarget(_build, dependency->_selector);
BuildAppendUniqueStrings(packages,
[self exportedPackagesForTarget: dependencyTarget]);
}
cached = [packages copy];
[_exportedPackagesByTargetName setObject: cached forKey: target->_name];
return cached;
}
- (OFArray *)exportedLinkArtifactsForTarget: (Target *)target
{
OFArray *cached = [_exportedLinkArtifactsByTargetName
objectForKey: target->_name];
OFMutableArray *artifacts;
OFString *artifactPath;
if (cached != nil)
return cached;
artifacts = [[OFMutableArray alloc] init];
artifactPath = BuildArtifactPath(_build, target);
if (artifactPath != nil)
[artifacts addObject: artifactPath];
for (BuildDependencySpec *dependency in target->_dependencies) {
Target *dependencyTarget;
if (!dependency->_publicDependency)
continue;
dependencyTarget = BuildLoadTarget(_build, dependency->_selector);
BuildAppendUniqueStrings(artifacts,
[self exportedLinkArtifactsForTarget: dependencyTarget]);
}
cached = [artifacts copy];
[_exportedLinkArtifactsByTargetName setObject: cached forKey: target->_name];
return cached;
}
- (BuildAction *)compileActionForTarget: (Target *)target
source: (OFString *)sourcePath
{
OFString *absoluteSource = BuildProjectAbsolutePath(_build, sourcePath);
OFString *objectPath = BuildObjectPath(_build, target, sourcePath);
OFString *depfilePath = [objectPath stringByAppendingString: @".d"];
OFString *signaturePath = [objectPath stringByAppendingString: @".signature"];
OFMutableArray *argv = [[OFMutableArray alloc] init];
OFMutableArray *inputs = [[OFMutableArray alloc] init];
OFMutableArray *defines = [[OFMutableArray alloc] init];
OFMutableArray *includeDirs = [[OFMutableArray alloc] init];
OFMutableArray *packages = [[OFMutableArray alloc] init];
OFMutableArray *flags = [[OFMutableArray alloc] init];
OFMutableArray *directDependencyInterfaces = [[OFMutableArray alloc] init];
BuildAction *action;
for (BuildDependencySpec *dependency in target->_dependencies) {
Target *dependencyTarget = BuildLoadTarget(_build, dependency->_selector);
[directDependencyInterfaces addObject: dependencyTarget];
}
[argv addObject: _build->_compiler];
[argv addObjectsFromArray: _build->_objfwCPPFlags];
[argv addObjectsFromArray: _build->_objfwObjCFlags];
[argv addObjectsFromArray: _build->_objfwArcFlags];
[argv addObjectsFromArray: _build->_modeCFlags];
[argv addObjectsFromArray: _build->_modeMFlags];
BuildAppendUniqueStrings(defines, _build->_globalDefines);
BuildAppendUniqueStrings(defines, target->_defines);
BuildAppendUniqueStrings(defines, target->_publicDefines);
BuildAppendUniqueStrings(packages, target->_packages);
BuildAppendUniqueStrings(packages, target->_publicPackages);
for (Target *dependencyTarget in directDependencyInterfaces)
BuildAppendUniqueStrings(defines,
[self exportedDefinesForTarget: dependencyTarget]);
for (Target *dependencyTarget in directDependencyInterfaces)
BuildAppendUniqueStrings(packages,
[self exportedPackagesForTarget: dependencyTarget]);
BuildAppendUniqueStrings(includeDirs, _build->_globalIncludeDirs);
BuildAppendUniqueStrings(includeDirs, target->_includeDirs);
BuildAppendUniqueStrings(includeDirs, target->_publicIncludeDirs);
for (Target *dependencyTarget in directDependencyInterfaces)
BuildAppendUniqueStrings(includeDirs,
[self exportedIncludeDirsForTarget: dependencyTarget]);
[argv addObjectsFromArray: BuildResolveDefineFlags(defines)];
[argv addObjectsFromArray: BuildResolveIncludeFlags(_build, includeDirs)];
[argv addObjectsFromArray: BuildResolvePackageCPPFlags(_build, packages)];
BuildAppendUniqueStrings(flags, _build->_globalCFlags);
BuildAppendUniqueStrings(flags, _build->_globalMFlags);
BuildAppendUniqueStrings(flags, target->_cFlags);
BuildAppendUniqueStrings(flags, target->_mFlags);
BuildAppendUniqueStrings(flags,
BuildCollectMatchingFlags(target->_fileCFlags, sourcePath));
BuildAppendUniqueStrings(flags,
BuildCollectMatchingFlags(target->_fileMFlags, sourcePath));
[argv addObjectsFromArray: flags];
if (target->_pmHeader != nil) {
[argv addObject: @"-include"];
[argv addObject: BuildProjectAbsolutePath(_build, target->_pmHeader)];
}
[argv addObjectsFromArray: [OFArray arrayWithObjects:
@"-MMD", @"-MF", depfilePath, @"-c", @"-o", objectPath, absoluteSource,
nil]];
BuildAppendUniqueString(inputs, absoluteSource);
if (target->_pmHeader != nil)
BuildAppendUniqueString(inputs,
BuildProjectAbsolutePath(_build, target->_pmHeader));
BuildAppendUniqueStrings(inputs, BuildResolvePathList(_build,
target->_extraInputs));
action = [BuildAction actionWithKey:
[@"compile:" stringByAppendingString: objectPath]
label: [OFString stringWithFormat: @"compile %@",
sourcePath]];
action->_argv = [argv copy];
action->_inputs = [inputs copy];
action->_outputs = [OFArray arrayWithObjects:
objectPath, depfilePath, signaturePath, nil];
action->_signaturePath = signaturePath;
action->_signature = BuildActionSignature(action);
action->_kind = ActionKindCommand;
action->_alwaysBuild = target->_alwaysBuild;
action->_environment = _build->_subprocessEnvironment;
action->_dynamicInputsProvider = [^ OFArray * {
return BuildParseDepfile(depfilePath);
} copy];
[_allActions addObject: action];
return action;
}
- (BuildAction *)finalActionForTarget: (Target *)target
{
BuildAction *cached = [_finalActionsByTargetName objectForKey: target->_name];
OFMutableArray *dependencyActions;
OFMutableArray *compileActions;
OFArray *sources;
OFString *artifactPath;
OFString *signaturePath;
BuildAction *finalAction;
if (cached != nil)
return cached;
target = BuildLoadTarget(_build, target->_selector);
dependencyActions = [[OFMutableArray alloc] init];
for (BuildDependencySpec *dependency in target->_dependencies) {
Target *dependencyTarget = BuildLoadTarget(_build, dependency->_selector);
[dependencyActions addObject: [self finalActionForTarget: dependencyTarget]];
}
sources = BuildResolvedSourcePaths(_build, target);
compileActions = [[OFMutableArray alloc] init];
for (OFString *sourcePath in sources)
[compileActions addObject: [self compileActionForTarget: target
source: sourcePath]];
artifactPath = BuildArtifactPath(_build, target);
signaturePath = (artifactPath != nil
? [artifactPath stringByAppendingString: @".signature"]
: nil);
finalAction = [BuildAction actionWithKey:
[@"target:" stringByAppendingString: target->_name]
label: [OFString stringWithFormat:
@"target %@", target->_name]];
[_finalActionsByTargetName setObject: finalAction forKey: target->_name];
[_allActions addObject: finalAction];
for (BuildAction *dependencyAction in dependencyActions)
BuildAddActionDependency(finalAction, dependencyAction);
for (BuildAction *compileAction in compileActions)
BuildAddActionDependency(finalAction, compileAction);
finalAction->_alwaysBuild = target->_alwaysBuild;
if (target->_kind == TargetKindPhony) {
finalAction->_kind = ActionKindNoOp;
finalAction->_signature = @"phony";
finalAction->_label = [OFString stringWithFormat: @"phony %@",
target->_name];
return finalAction;
}
if (target->_kind == TargetKindStaticLibrary) {
OFMutableArray *argv = [[OFMutableArray alloc] init];
OFMutableArray *inputs = [[OFMutableArray alloc] init];
[argv addObjectsFromArray: [OFArray arrayWithObjects:
_build->_archiver, @"rcs", artifactPath, nil]];
for (BuildAction *compileAction in compileActions) {
[argv addObject: [compileAction->_outputs objectAtIndex: 0]];
[inputs addObject: [compileAction->_outputs objectAtIndex: 0]];
}
finalAction->_argv = [argv copy];
finalAction->_inputs = [inputs copy];
finalAction->_outputs = [OFArray arrayWithObjects:
artifactPath, signaturePath, nil];
finalAction->_signaturePath = signaturePath;
finalAction->_kind = ActionKindCommand;
finalAction->_label = [OFString stringWithFormat: @"archive %@",
BuildDisplayPath(_build, artifactPath)];
finalAction->_signature = BuildActionSignature(finalAction);
finalAction->_environment = _build->_subprocessEnvironment;
return finalAction;
}
{
OFMutableArray *argv = [[OFMutableArray alloc] init];
OFMutableArray *inputs = [[OFMutableArray alloc] init];
OFMutableArray *libraryDirs = [[OFMutableArray alloc] init];
OFMutableArray *linkFlags = [[OFMutableArray alloc] init];
OFMutableArray *linkArtifacts = [[OFMutableArray alloc] init];
OFMutableArray *packages = [[OFMutableArray alloc] init];
OFMutableArray *objfwStaticLibs = [[OFMutableArray alloc] init];
[argv addObject: _build->_compiler];
[argv addObject: @"-o"];
[argv addObject: artifactPath];
for (BuildAction *compileAction in compileActions) {
OFString *objectPath = [compileAction->_outputs objectAtIndex: 0];
[argv addObject: objectPath];
[inputs addObject: objectPath];
}
for (BuildDependencySpec *dependency in target->_dependencies) {
Target *dependencyTarget = BuildLoadTarget(_build, dependency->_selector);
BuildAppendUniqueStrings(linkArtifacts,
[self exportedLinkArtifactsForTarget: dependencyTarget]);
BuildAppendUniqueStrings(packages,
[self exportedPackagesForTarget: dependencyTarget]);
}
for (OFString *linkArtifact in linkArtifacts) {
[argv addObject: linkArtifact];
[inputs addObject: linkArtifact];
}
BuildAppendUniqueStrings(packages, target->_packages);
BuildAppendUniqueStrings(packages, target->_publicPackages);
[libraryDirs addObjectsFromArray:
BuildResolveLibraryDirectoryFlags(_build, target->_libraryDirs)];
[linkFlags addObjectsFromArray: _build->_globalLDFlags];
[linkFlags addObjectsFromArray: _build->_modeLDFlags];
[linkFlags addObjectsFromArray: target->_ldFlags];
BuildAppendUniqueStrings(linkFlags,
BuildResolvePackageLDFlags(_build, packages));
BuildAppendUniqueStrings(linkFlags, _build->_objfwLDFlags);
if (packages.count > 0)
BuildAppendUniqueStrings(objfwStaticLibs,
BuildResolvePackageStaticLibs(_build, packages));
else
BuildAppendUniqueStrings(objfwStaticLibs, _build->_objfwStaticLibs);
[argv addObjectsFromArray: libraryDirs];
[argv addObjectsFromArray: BuildResolveLinkFlags(target->_links)];
[argv addObjectsFromArray: objfwStaticLibs];
[argv addObjectsFromArray: linkFlags];
if (target->_kind == TargetKindSharedLibrary)
[argv addObject: @"-shared"];
finalAction->_argv = [argv copy];
finalAction->_inputs = [inputs copy];
finalAction->_outputs = [OFArray arrayWithObjects:
artifactPath, signaturePath, nil];
finalAction->_signaturePath = signaturePath;
finalAction->_kind = ActionKindCommand;
finalAction->_label = [OFString stringWithFormat: @"link %@",
BuildDisplayPath(_build, artifactPath)];
finalAction->_signature = BuildActionSignature(finalAction);
finalAction->_environment = _build->_subprocessEnvironment;
}
return finalAction;
}
- (void)enqueueReadyActions
{
for (BuildAction *action in _allActions) {
if (action->_completed || action->_remainingDependencies > 0)
continue;
if (action->_process != nil)
continue;
if ([_readyActions containsObject: action])
continue;
[_readyActions addObject: action];
}
}
- (void)completeAction: (BuildAction *)action
success: (bool)success
skipped: (bool)skipped
{
if (action->_completed)
return;
action->_completed = true;
action->_succeeded = success;
action->_skipped = skipped;
if (action->_process != nil) {
action->_process = nil;
if (_activeActions > 0)
_activeActions--;
}
if (!success) {
_failed = true;
_exitStatus = 1;
}
for (BuildAction *dependent in action->_dependents) {
if (dependent->_remainingDependencies > 0)
dependent->_remainingDependencies--;
}
[self enqueueReadyActions];
[self drainReadyQueue];
}
- (void)finishIfPossible
{
if (_activeActions > 0 || _readyActions.count > 0)
return;
if (!_failed && _runTargetName != nil && !_launchedRunTarget) {
Target *runTarget = [_build targetNamed: _runTargetName];
OFString *artifactPath;
_launchedRunTarget = true;
if (runTarget == nil || runTarget->_kind != TargetKindBinary) {
[OFStdErr writeFormat:
@"Cannot run %@ because it is not a binary target.\n",
_runTargetName];
_completion(1);
return;
}
artifactPath = BuildArtifactPath(_build, runTarget);
@try {
_runProcess = [OFSubprocess subprocessWithProgram: artifactPath
arguments: _runArguments];
} @catch (id exception) {
[OFStdErr writeFormat: @"Run failed to launch %@: %@\n",
artifactPath, exception];
_completion(1);
return;
}
[OFStdOut writeFormat: @"Running %@\n",
BuildDisplayPath(_build, artifactPath)];
[_runProcess closeForWriting];
[self performSelector: @selector(pollRunProcess) afterDelay: 0];
return;
}
_completion(_exitStatus);
}
- (void)launchAction: (BuildAction *)action
{
OFFileManager *fileManager = [OFFileManager defaultManager];
if (action->_kind == ActionKindNoOp) {
if (_build->_verbose)
[OFStdOut writeFormat: @"%@: no-op\n", action->_label];
[self completeAction: action success: true skipped: true];
return;
}
if (BuildActionIsUpToDate(action)) {
if (_build->_verbose)
[OFStdOut writeFormat: @"%@: up to date\n", action->_label];
[self completeAction: action success: true skipped: true];
return;
}
for (OFString *output in action->_outputs) {
OFString *directory = output.stringByDeletingLastPathComponent;
if (directory.length > 0)
[fileManager createDirectoryAtPath: directory createParents: true];
}
if (_build->_verbose)
[OFStdOut writeFormat: @"%@: %@\n", action->_label,
[action->_argv componentsJoinedByString: @" "]];
@try {
action->_process = [OFSubprocess subprocessWithProgram:
[action->_argv objectAtIndex: 0]
programName:
[action->_argv objectAtIndex: 0]
arguments:
[action->_argv objectsInRange: OFMakeRange(1,
action->_argv.count - 1)]
environment:
action->_environment];
} @catch (id exception) {
[OFStdErr writeFormat: @"%@: failed to launch: %@\n",
action->_label, exception];
[self completeAction: action success: false skipped: false];
[self finishIfPossible];
return;
}
_activeActions++;
[action->_process closeForWriting];
[self performSelector: @selector(pollActionOutput:)
withObject: action
afterDelay: 0];
}
- (void)pollActionOutput: (BuildAction *)action
{
while (true) {
OFString *line;
@try {
line = [action->_process tryReadLineWithEncoding:
OFStringEncodingUTF8];
} @catch (id exception) {
[OFStdErr writeFormat: @"%@: %@\n", action->_label, exception];
[self completeAction: action success: false skipped: false];
[self finishIfPossible];
return;
}
if (line == nil)
break;
[OFStdOut writeFormat: @"%@: %@\n", action->_label, line];
}
if (action->_process.atEndOfStream) {
if ([action->_process waitForTermination] != 0) {
[OFStdErr writeFormat: @"%@: command failed\n", action->_label];
[self completeAction: action success: false skipped: false];
[self finishIfPossible];
return;
}
[action->_signature writeToFile: action->_signaturePath];
[self completeAction: action success: true skipped: false];
[self finishIfPossible];
return;
}
[self performSelector: @selector(pollActionOutput:)
withObject: action
afterDelay: 0.02];
}
- (void)pollRunProcess
{
while (true) {
OFString *line;
@try {
line = [_runProcess tryReadLineWithEncoding: OFStringEncodingUTF8];
} @catch (id exception) {
[OFStdErr writeFormat:
@"Run failed while reading output: %@\n", exception];
_completion(1);
return;
}
if (line == nil)
break;
[OFStdOut writeLine: line];
}
if (_runProcess.atEndOfStream) {
_completion([_runProcess waitForTermination]);
return;
}
[self performSelector: @selector(pollRunProcess) afterDelay: 0.02];
}
- (void)drainReadyQueue
{
while (!_failed && _activeActions < _build->_jobs && _readyActions.count > 0) {
BuildAction *action = [_readyActions objectAtIndex: 0];
[_readyActions removeObjectAtIndex: 0];
[self launchAction: action];
}
[self finishIfPossible];
}
- (void)start
{
OFMutableArray *targets = [[OFMutableArray alloc] init];
if (_requestedTargetNames.count == 0) {
Target *defaultTarget = [_build targetNamed: @"mainTarget"];
if (defaultTarget == nil) {
[OFStdErr writeLine:
@"No target specified and no mainTarget target is defined."];
_completion(1);
return;
}
[targets addObject: defaultTarget];
} else {
for (OFString *targetName in _requestedTargetNames) {
Target *target = [_build targetNamed: targetName];
if (target == nil) {
[OFStdErr writeFormat: @"Unknown target: %@\n", targetName];
_completion(1);
return;
}
[targets addObject: target];
}
}
if (_runTargetName != nil) {
Target *runTarget = [_build targetNamed: _runTargetName];
if (runTarget == nil) {
[OFStdErr writeFormat: @"Unknown run target: %@\n", _runTargetName];
_completion(1);
return;
}
if (![targets containsObject: runTarget])
[targets addObject: runTarget];
}
for (Target *target in targets)
[self finalActionForTarget: target];
for (BuildAction *action in _allActions)
action->_remainingDependencies = action->_dependencies.count;
[self enqueueReadyActions];
[self drainReadyQueue];
}
@end
static void BuildListTargets(Build *build)
{
OFMutableArray *targets = [[OFMutableArray alloc] init];
for (OFString *selectorName in BuildKnownTargetSelectorNames(build))
[targets addObject: BuildLoadTarget(build, sel_registerName(
selectorName.UTF8String))];
[OFStdOut writeLine: @"Available targets:"];
for (Target *target in targets) {
OFString *kind;
switch (target->_kind) {
case TargetKindStaticLibrary:
kind = @"static";
break;
case TargetKindSharedLibrary:
kind = @"shared";
break;
case TargetKindPhony:
kind = @"phony";
break;
case TargetKindBinary:
default:
kind = @"binary";
break;
}
[OFStdOut writeFormat: @" %@ (%@)",
target.name, kind];
if (target->_group != nil)
[OFStdOut writeFormat: @" group=%@", target->_group];
if (target->_defaultTarget != 0)
[OFStdOut writeFormat: @" default=yes"];
if (target.artifactName != nil && target->_kind != TargetKindPhony)
[OFStdOut writeFormat: @" artifact=%@",
BuildDisplayPath(build, BuildArtifactPath(build, target))];
[OFStdOut writeLine: @""];
}
}
static void BuildClean(Build *build)
{
OFFileManager *fileManager = [OFFileManager defaultManager];
if ([fileManager fileExistsAtPath: build->_buildBaseRoot])
[fileManager removeItemAtPath: build->_buildBaseRoot];
[OFStdOut writeFormat: @"Removed %@\n",
BuildDisplayPath(build, build->_buildBaseRoot)];
}
static void BuildPrintUsage(void)
{
[OFStdOut writeLine:
@"Usage: build [list|clean|run] [targets...] [--mode MODE] [-j JOBS] [-v] [-- args...]"];
[OFStdOut writeLine:
@" build Build mainTarget by default"];
[OFStdOut writeLine:
@" build list List target methods exported by Build"];
[OFStdOut writeLine:
@" build clean Remove the build directory"];
[OFStdOut writeLine:
@" build run [target] Build and run a binary target"];
}
@interface App ()
{
BuildEngine *_engine;
}
@end
@implementation App
- (void)applicationDidFinishLaunching: (OFNotification *)notification
{
Build *build;
OFArray *arguments = [OFApplication arguments] ?: [OFArray array];
OFMutableArray *targetNames = [[OFMutableArray alloc] init];
OFMutableArray *runArguments = [[OFMutableArray alloc] init];
OFString *command = @"build";
OFString *runTargetName = nil;
bool parsingRunArguments = false;
@try {
build = [[Build alloc] init];
} @catch (id exception) {
[OFStdErr writeFormat: @"Failed to initialize build: %@\n", exception];
[OFApplication terminateWithStatus: 1];
return;
}
for (size_t index = 0; index < arguments.count; index++) {
OFString *argument = [arguments objectAtIndex: index];
if (parsingRunArguments) {
[runArguments addObject: argument];
continue;
}
if ([argument isEqual: @"--"]) {
parsingRunArguments = true;
continue;
}
if ([argument isEqual: @"--help"] || [argument isEqual: @"help"]) {
BuildPrintUsage();
[OFApplication terminate];
return;
}
if ([argument isEqual: @"-v"] || [argument isEqual: @"--verbose"]) {
build.verbose = true;
continue;
}
if ([argument isEqual: @"-j"] || [argument isEqual: @"--jobs"]) {
if (index + 1 >= arguments.count) {
[OFStdErr writeLine: @"Missing value for jobs option."];
[OFApplication terminateWithStatus: 1];
return;
}
build.jobs = [[arguments objectAtIndex: ++index]
unsignedLongLongValue];
continue;
}
if ([argument hasPrefix: @"--jobs="]) {
build.jobs = [[argument substringFromIndex: 7]
unsignedLongLongValue];
continue;
}
if ([argument isEqual: @"--mode"]) {
if (index + 1 >= arguments.count) {
[OFStdErr writeLine: @"Missing value for mode option."];
[OFApplication terminateWithStatus: 1];
return;
}
build.mode = [arguments objectAtIndex: ++index];
continue;
}
if ([argument hasPrefix: @"--mode="]) {
build.mode = [argument substringFromIndex: 7];
continue;
}
if ([argument isEqual: @"list"] || [argument isEqual: @"clean"] ||
[argument isEqual: @"run"]) {
command = argument;
continue;
}
if ([command isEqual: @"run"] && runTargetName == nil) {
runTargetName = argument;
continue;
}
[targetNames addObject: argument];
}
if ([command isEqual: @"list"]) {
BuildListTargets(build);
[OFApplication terminate];
return;
}
if ([command isEqual: @"clean"]) {
BuildClean(build);
[OFApplication terminate];
return;
}
if ([command isEqual: @"run"] && runTargetName == nil)
runTargetName = @"app";
if ([command isEqual: @"run"] && targetNames.count == 0 && runTargetName != nil)
[targetNames addObject: runTargetName];
_engine = [[BuildEngine alloc] initWithBuild: build
targetNames: targetNames
runTargetName: runTargetName
runArguments: runArguments
completion: ^ (int status) {
_engine = nil;
[OFApplication terminateWithStatus: status];
}];
[_engine start];
}
@end
#ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wnullability-completeness-on-arrays"
# pragma clang diagnostic ignored "-Wnullability-inferred-on-nested-type"
#endif
OF_APPLICATION_DELEGATE(App);
#ifdef __clang__
# pragma clang diagnostic pop
#endif
#pragma clang assume_nonnull end
#endif
#include "build.h"
@implementation Build
- (Target *)mainTarget
{
Target *target = BUILD_TARGET.phony.defaultTarget;
return [target dependsOn: @[ self.app, self.asyncRuntimeTests ]];
}
- (Target *)utilities
{
Target *target = BUILD_TARGET.staticLibrary;
[target outputName: @"Utilities"];
[target pmHeader: @"src/Utilities/common.h"];
return [target files: @[ @"src/Utilities/**.m" ]];
}
- (Target *)async
{
Target *target = BUILD_TARGET.staticLibrary;
[target outputName: @"Async"];
[target publicPackages: @[ @"ObjFWTLS" ]];
[target publiclyDependsOn: @[ self.utilities ]];
[target files: @[
@"src/Async/Coroutine.m",
@"src/Async/**.m|src/Async/Coroutine.m"
]];
return [target fileMFlags: @"src/Async/Coroutine.m"
flags: @[ @"-fno-objc-arc" ]];
}
- (Target *)app
{
Target *target = BUILD_TARGET.binary;
[target outputName: @"App"];
[target dependsOn: @[ self.async ]];
[target pmHeader: @"src/Utilities/common.h"];
return [target files: @[ @"src/App/**.m" ]];
}
- (Target *)asyncRuntimeTests
{
Target *target = BUILD_TARGET.binary;
[target outputName: @"async-runtime-tests"];
[target group: @"tests"];
[target dependsOn: @[ self.async ]];
[target pmHeader: @"src/Utilities/common.h"];
[target mFlags: @[
@"-Wno-nonnull",
@"-Wno-nullability-completeness",
@"-Wno-nullable-to-nonnull-conversion"
]];
[target links: @[ @"objfwtest", @"objfwhid" ]];
return [target files: @[ @"tests/*.m" ]];
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment