diff --git a/llvm/test/tools/llvm-libtool-darwin/Inputs/DependencyDump.py b/llvm/test/tools/llvm-libtool-darwin/Inputs/DependencyDump.py new file mode 100755 --- /dev/null +++ b/llvm/test/tools/llvm-libtool-darwin/Inputs/DependencyDump.py @@ -0,0 +1,26 @@ +# +# Dump the dependency file (produced with -dependency_info) to text +# format for testing purposes. +# + +import sys + +f = open(sys.argv[1], "rb") +byte = f.read(1) +while byte != b'': + if byte == b'\x00': + sys.stdout.write("version: ") + elif byte == b'\x10': + sys.stdout.write("input-file: ") + elif byte == b'\x11': + sys.stdout.write("not-found: ") + elif byte == b'\x40': + sys.stdout.write("output-file: ") + byte = f.read(1) + while byte != b'\x00': + sys.stdout.write(byte.decode("ascii")) + byte = f.read(1) + sys.stdout.write("\n") + byte = f.read(1) + +f.close() diff --git a/llvm/test/tools/llvm-libtool-darwin/dependency-info.test b/llvm/test/tools/llvm-libtool-darwin/dependency-info.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-libtool-darwin/dependency-info.test @@ -0,0 +1,19 @@ +## This test validates the format of the dependency info file +# RUN: rm -rf %t; mkdir -p %t + +# RUN: yaml2obj %S/Inputs/input1.yaml -o %t/foo.o +# RUN: llvm-libtool-darwin -static -o %t/libfirst.a %t/foo.o -dependency_info %t/simple.dat +# RUN: %python %S/Inputs/DependencyDump.py %t/simple.dat | FileCheck --check-prefix=SIMPLE %s + +# RUN: llvm-libtool-darwin -static -o %t/second.lib %t/foo.o -lfirst -L/missing/directory/without/lib -L%t -dependency_info %t/lib.dat +# RUN: %python %S/Inputs/DependencyDump.py %t/lib.dat | FileCheck --check-prefix=LIB %s + +# SIMPLE: version: llvm-libtool-darwin +# SIMPLE: input-file: {{.+}}foo.o +# SIMPLE: output-file: {{.+}}libfirst.a + +# LIB: version: llvm-libtool-darwin +# LIB: input-file: {{.+}}foo.o +# LIB: input-file: {{.+}}libfirst.a +# LIB: not-found: {{.+}}missing{{.+}}directory{{.+}}without{{.+}}lib{{.+}}libfirst.a +# LIB: output-file: {{.+}}second.lib diff --git a/llvm/tools/llvm-libtool-darwin/DependencyInfo.h b/llvm/tools/llvm-libtool-darwin/DependencyInfo.h new file mode 100644 --- /dev/null +++ b/llvm/tools/llvm-libtool-darwin/DependencyInfo.h @@ -0,0 +1,79 @@ +//===-- DependencyInfo.h --------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/WithColor.h" +#include "llvm/Support/raw_ostream.h" + +#include + +class DependencyInfo { +public: + explicit DependencyInfo(std::string DependencyInfoPath) + : Active(!DependencyInfoPath.empty()), + DependencyInfoPath(DependencyInfoPath) {} + + void addMissingInput(llvm::StringRef Path) { + if (Active) + NotFounds.insert(Path.str()); + } + + // Writes the dependencies to specified path. The content is first sorted by + // OpCode and then by the filename (in alphabetical order). + void write(llvm::Twine Version, const std::vector &Inputs, + std::string Output) { + if (!Active) + return; + + std::error_code EC; + llvm::raw_fd_ostream OS(DependencyInfoPath, EC, llvm::sys::fs::OF_None); + if (EC) { + llvm::WithColor::defaultErrorHandler(llvm::createStringError( + EC, + "failed to write to " + DependencyInfoPath + ": " + EC.message())); + return; + } + + auto AddDep = [&OS](DependencyInfoOpcode Opcode, + const llvm::StringRef &Path) { + OS << static_cast(Opcode); + OS << Path; + OS << '\0'; + }; + + AddDep(DependencyInfoOpcode::Tool, Version.str()); + + // Sort the input by its names. + std::vector InputNames; + InputNames.reserve(Inputs.size()); + for (const auto &F : Inputs) + InputNames.push_back(F); + llvm::sort(InputNames); + + for (const auto &In : InputNames) + AddDep(DependencyInfoOpcode::InputFound, In); + + for (const std::string &F : NotFounds) + AddDep(DependencyInfoOpcode::InputMissing, F); + + AddDep(DependencyInfoOpcode::Output, Output); + } + +private: + enum DependencyInfoOpcode : uint8_t { + Tool = 0x00, + InputFound = 0x10, + InputMissing = 0x11, + Output = 0x40, + }; + + bool Active = false; + const std::string DependencyInfoPath; + std::set NotFounds; +}; diff --git a/llvm/tools/llvm-libtool-darwin/llvm-libtool-darwin.cpp b/llvm/tools/llvm-libtool-darwin/llvm-libtool-darwin.cpp --- a/llvm/tools/llvm-libtool-darwin/llvm-libtool-darwin.cpp +++ b/llvm/tools/llvm-libtool-darwin/llvm-libtool-darwin.cpp @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +#include "DependencyInfo.h" #include "llvm/BinaryFormat/Magic.h" #include "llvm/IR/LLVMContext.h" #include "llvm/Object/ArchiveWriter.h" @@ -87,6 +88,12 @@ " libraries"), cl::Prefix, cl::cat(LibtoolCategory)); +static cl::opt DependencyInfoPath( + "dependency_info", + cl::desc("Write an Xcode dependency info file describing the dependencies " + "of the created library"), + cl::cat(LibtoolCategory)); + static cl::opt VersionOption("V", cl::desc("Print the version number and exit"), cl::cat(LibtoolCategory)); @@ -109,6 +116,8 @@ "/usr/local/lib", }; +std::unique_ptr GlobalDependencyInfo; + struct Config { bool Deterministic = true; // Updated by 'D' and 'U' modifiers. uint32_t ArchCPUType; @@ -116,7 +125,6 @@ }; static Expected searchForFile(const Twine &FileName) { - auto FindLib = [FileName](ArrayRef SearchDirs) -> Optional { for (StringRef Dir : SearchDirs) { @@ -125,6 +133,8 @@ if (sys::fs::exists(Path)) return std::string(Path); + + GlobalDependencyInfo->addMissingInput(Path); } return None; }; @@ -652,6 +662,8 @@ return C; } + GlobalDependencyInfo = std::make_unique(DependencyInfoPath); + if (OutputFile.empty()) { std::string Error; raw_string_ostream Stream(Error); @@ -686,6 +698,9 @@ MachO::getArchitectureFromName(ArchType)); } + GlobalDependencyInfo->write("llvm-libtool-darwin " LLVM_VERSION_STRING, + InputFiles, OutputFile); + return C; }