diff --git a/include/llbuild/BuildSystem/BuildFile.h b/include/llbuild/BuildSystem/BuildFile.h index 39ad02c3..90748e39 100644 --- a/include/llbuild/BuildSystem/BuildFile.h +++ b/include/llbuild/BuildSystem/BuildFile.h @@ -138,7 +138,7 @@ class BuildFileDelegate { /// /// \param isImplicit Whether the node is an implicit one (created as a side /// effect of being declared by a command). - virtual std::unique_ptr lookupNode(StringRef name, + virtual std::unique_ptr createNode(StringRef name, bool isImplicit=false) = 0; }; diff --git a/include/llbuild/BuildSystem/BuildSystem.h b/include/llbuild/BuildSystem/BuildSystem.h index bc372fee..c4c18b7a 100644 --- a/include/llbuild/BuildSystem/BuildSystem.h +++ b/include/llbuild/BuildSystem/BuildSystem.h @@ -36,6 +36,7 @@ namespace buildsystem { class BuildDescription; class BuildKey; +class BuildNode; class BuildValue; class Command; class Node; @@ -312,6 +313,8 @@ class BuildSystem { /// @} ShellCommandHandler* resolveShellCommandHandler(ShellCommand* command); + + BuildNode *lookupNode(StringRef name); }; } diff --git a/lib/BuildSystem/BuildFile.cpp b/lib/BuildSystem/BuildFile.cpp index f13ee327..1f725460 100644 --- a/lib/BuildSystem/BuildFile.cpp +++ b/lib/BuildSystem/BuildFile.cpp @@ -368,7 +368,7 @@ class BuildFileImpl { return it->second.get(); // Otherwise, ask the delegate to create the node. - auto node = delegate.lookupNode(name, isImplicit); + auto node = delegate.createNode(name, isImplicit); assert(node); auto result = node.get(); nodes[name] = std::move(node); diff --git a/lib/BuildSystem/BuildSystem.cpp b/lib/BuildSystem/BuildSystem.cpp index 46175ad0..ba99987a 100644 --- a/lib/BuildSystem/BuildSystem.cpp +++ b/lib/BuildSystem/BuildSystem.cpp @@ -125,7 +125,7 @@ class BuildSystemFileDelegate : public BuildFileDelegate { virtual void loadedCommand(StringRef name, const Command& target) override; - virtual std::unique_ptr lookupNode(StringRef name, + virtual std::unique_ptr createNode(StringRef name, bool isImplicit=false) override; /// @} @@ -162,6 +162,8 @@ class BuildSystemEngineDelegate : public BuildEngineDelegate { BuildSystemImpl& getBuildSystem() { return system; } + + BuildNode *lookupNode(StringRef name); }; class BuildSystemImpl { @@ -274,8 +276,10 @@ class BuildSystemImpl { const Twine& message) { getDelegate().error(filename, at, message); } + + BuildNode* lookupNode(StringRef name); - std::unique_ptr lookupNode(StringRef name, + std::unique_ptr createNode(StringRef name, bool isImplicit); uint32_t getMergedSchemaVersion() { @@ -1695,7 +1699,26 @@ class BuildSystemRule : public Rule { - +BuildNode *BuildSystemEngineDelegate::lookupNode(StringRef name) { + // Find the node. + auto it = getBuildDescription().getNodes().find(name); + BuildNode* node; + if (it != getBuildDescription().getNodes().end()) { + node = static_cast(it->second.get()); + } else { + auto it = dynamicNodes.find(name); + if (it != dynamicNodes.end()) { + node = it->second.get(); + } else { + // Create nodes on the fly for any unknown ones. + auto nodeOwner = system.createNode( + name, /*isImplicit=*/true); + node = nodeOwner.get(); + dynamicNodes[name] = std::move(nodeOwner); + } + } + return node; +} std::unique_ptr BuildSystemEngineDelegate::lookupRule(const KeyType& keyData) { // Decode the key. @@ -1858,22 +1881,7 @@ std::unique_ptr BuildSystemEngineDelegate::lookupRule(const KeyType& keyDa case BuildKey::Kind::Node: { // Find the node. - auto it = getBuildDescription().getNodes().find(key.getNodeName()); - BuildNode* node; - if (it != getBuildDescription().getNodes().end()) { - node = static_cast(it->second.get()); - } else { - auto it = dynamicNodes.find(key.getNodeName()); - if (it != dynamicNodes.end()) { - node = it->second.get(); - } else { - // Create nodes on the fly for any unknown ones. - auto nodeOwner = system.lookupNode( - key.getNodeName(), /*isImplicit=*/true); - node = nodeOwner.get(); - dynamicNodes[key.getNodeName()] = std::move(nodeOwner); - } - } + BuildNode* node = lookupNode(key.getNodeName()); // Create the rule used to construct this node. // @@ -2051,8 +2059,12 @@ void BuildSystemEngineDelegate::error(const Twine& message) { #pragma mark - BuildSystemImpl implementation +BuildNode *BuildSystemImpl::lookupNode(StringRef name) { + return engineDelegate.lookupNode(name); +} + std::unique_ptr -BuildSystemImpl::lookupNode(StringRef name, bool isImplicit) { +BuildSystemImpl::createNode(StringRef name, bool isImplicit) { if (name.endswith("/")) { return BuildNode::makeDirectory(name); } @@ -4084,9 +4096,9 @@ void BuildSystemFileDelegate::loadedCommand(StringRef name, } std::unique_ptr -BuildSystemFileDelegate::lookupNode(StringRef name, +BuildSystemFileDelegate::createNode(StringRef name, bool isImplicit) { - return system.lookupNode(name, isImplicit); + return system.createNode(name, isImplicit); } } @@ -4169,6 +4181,10 @@ BuildSystem::resolveShellCommandHandler(ShellCommand* command) { return static_cast(impl)->resolveShellCommandHandler(command); } +BuildNode* BuildSystem::lookupNode(StringRef name) { + return static_cast(impl)->lookupNode(name); +} + // This function checks if the given path is prefixed by another path. bool llbuild::buildsystem::pathIsPrefixedByPath(std::string path, std::string prefixPath) { diff --git a/lib/Commands/BuildSystemCommand.cpp b/lib/Commands/BuildSystemCommand.cpp index 96f28808..46a4cd96 100644 --- a/lib/Commands/BuildSystemCommand.cpp +++ b/lib/Commands/BuildSystemCommand.cpp @@ -92,7 +92,7 @@ class ParseBuildFileDelegate : public BuildFileDelegate { virtual void loadedDefaultTarget(StringRef target) override; - virtual std::unique_ptr lookupNode(StringRef name, + virtual std::unique_ptr createNode(StringRef name, bool isImplicit) override; virtual void loadedCommand(StringRef name, @@ -368,7 +368,7 @@ void ParseBuildFileDelegate::loadedDefaultTarget(StringRef target) { } std::unique_ptr -ParseBuildFileDelegate::lookupNode(StringRef name, +ParseBuildFileDelegate::createNode(StringRef name, bool isImplicit) { if (!isImplicit) { if (showOutput) { diff --git a/products/libllbuild/BuildSystem-C-API.cpp b/products/libllbuild/BuildSystem-C-API.cpp index d755a0ff..25d42992 100644 --- a/products/libllbuild/BuildSystem-C-API.cpp +++ b/products/libllbuild/BuildSystem-C-API.cpp @@ -719,6 +719,9 @@ class CAPIExternalCommand : public ExternalCommand { /// The paths to the dependency output files, if used. SmallVector depsPaths{}; + + /// Names of output nodes of this command. + SmallVector outputNodeNames{}; /// The working directory used to resolve relative paths in dependency files. std::string workingDirectory; @@ -893,6 +896,9 @@ class CAPIExternalCommand : public ExternalCommand { SmallString wd = value; llvm::sys::fs::make_absolute(wd); workingDirectory = StringRef(wd); + } else if (name == "outputs") { + outputNodeNames.clear(); + outputNodeNames.emplace_back(value); } else { return false; } @@ -904,6 +910,9 @@ class CAPIExternalCommand : public ExternalCommand { if (name == "deps") { depsPaths.clear(); depsPaths.insert(depsPaths.begin(), values.begin(), values.end()); + } else if (name == "outputs") { + outputNodeNames.clear(); + outputNodeNames.insert(outputNodeNames.begin(), values.begin(), values.end()); } else { return false; } @@ -943,6 +952,10 @@ class CAPIExternalCommand : public ExternalCommand { virtual void startExternalCommand(BuildSystem& system, core::TaskInterface ti) override { + outputs.reserve(outputNodeNames.size()); + for (auto name: outputNodeNames) { + outputs.push_back(system.lookupNode(name)); + } cAPIDelegate.start(cAPIDelegate.context, (llb_buildsystem_command_t*)this, (llb_buildsystem_interface_t*)&system, diff --git a/products/libllbuild/include/llbuild/llbuild-defines.h b/products/libllbuild/include/llbuild/llbuild-defines.h index b095f0de..52a695dd 100644 --- a/products/libllbuild/include/llbuild/llbuild-defines.h +++ b/products/libllbuild/include/llbuild/llbuild-defines.h @@ -84,6 +84,7 @@ /// compile for multiple versions of the API. /// /// Version History: +/// 18: Added support for configuring outputs of dynamic tasks via the C API. /// /// 17: Added `llb_buildsystem_dependency_data_format_makefile_ignoring_subsequent_outputs` /// @@ -120,6 +121,6 @@ /// 1: Added `environment` parameter to llb_buildsystem_invocation_t. /// /// 0: Pre-history -#define LLBUILD_C_API_VERSION 17 +#define LLBUILD_C_API_VERSION 18 #endif diff --git a/products/llbuildSwift/BuildSystemBindings.swift b/products/llbuildSwift/BuildSystemBindings.swift index f85c60bc..140bf62d 100644 --- a/products/llbuildSwift/BuildSystemBindings.swift +++ b/products/llbuildSwift/BuildSystemBindings.swift @@ -239,6 +239,9 @@ public protocol ExternalCommand: AnyObject { /// This is checked to determine if the command needs to rebuild versus the last time it was run. func getSignature(_ command: Command) -> [UInt8] + /// Outputs nodes of the command. + var outputs: [String] { get } + /// Paths to files that contain discovered dependencies after command executed successfully for subsequent builds. var dependencyPaths: [String] { get } @@ -300,6 +303,12 @@ public protocol ExternalCommand: AnyObject { func execute(_ command: Command, _ commandInterface: BuildSystemCommandInterface, _ jobContext: JobContext) -> CommandResult } +public extension ExternalCommand { + var outputs: [String] { + [] + } +} + public protocol ExternalDetachedCommand: AnyObject { /// Whether the command should run outside the execution lanes. /// If true the build system will call `executeDetached` and `cancelDetached`. @@ -457,6 +466,24 @@ private final class CommandWrapper { } single(context, workingDirectoryKey, workingDirectoryValue) } + if !command.outputs.isEmpty { + var outputs: [llb_data_t] = [] + for output in command.outputs { + outputs.append(copiedDataFromBytes(Array(output.utf8))) + } + var key = copiedDataFromBytes(Array("outputs".utf8)) + defer { + llb_data_destroy(&key) + for index in outputs.indices { + llb_data_destroy(&outputs[index]) + } + } + if outputs.count == 1 { + single(context, key, outputs[0]) + } else { + collection(context, key, &outputs, outputs.count) + } + } } func getSignature(_: OpaquePointer, _ data: UnsafeMutablePointer) {