Skip to content

Commit

Permalink
[InstallAPI] Pick up input headers by directory traversal (#94508)
Browse files Browse the repository at this point in the history
Match TAPI behavior and allow input headers to be resolved via a passed
directory, which is expected to be a library sitting in a build
directory.
  • Loading branch information
cyndyishida authored Jun 14, 2024
1 parent 445fc51 commit feed66f
Show file tree
Hide file tree
Showing 19 changed files with 703 additions and 24 deletions.
2 changes: 2 additions & 0 deletions clang/include/clang/Basic/DiagnosticInstallAPIKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ def err_unsupported_environment : Error<"environment '%0' is not supported: '%1'
def err_unsupported_os : Error<"os '%0' is not supported: '%1'">;
def err_cannot_read_input_list : Error<"could not read %0 input list '%1': %2">;
def err_invalid_label: Error<"label '%0' is reserved: use a different label name for -X<label>">;
def err_directory_scanning: Error<"could not read directory '%0': %1">;
def err_more_than_one_library: Error<"more than one framework/dynamic library found">;
} // end of command line category.

let CategoryName = "Verification" in {
Expand Down
81 changes: 81 additions & 0 deletions clang/include/clang/InstallAPI/DirectoryScanner.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
//===- InstallAPI/DirectoryScanner.h ----------------------------*- C++ -*-===//
//
// 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
//
//===----------------------------------------------------------------------===//
///
/// The DirectoryScanner for collecting library files on the file system.
///
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_INSTALLAPI_DIRECTORYSCANNER_H
#define LLVM_CLANG_INSTALLAPI_DIRECTORYSCANNER_H

#include "clang/Basic/FileManager.h"
#include "clang/InstallAPI/Library.h"

namespace clang::installapi {

enum ScanMode {
/// Scanning Framework directory.
ScanFrameworks,
/// Scanning Dylib directory.
ScanDylibs,
};

class DirectoryScanner {
public:
DirectoryScanner(FileManager &FM, ScanMode Mode = ScanMode::ScanFrameworks)
: FM(FM), Mode(Mode) {}

/// Scan for all input files throughout directory.
///
/// \param Directory Path of input directory.
llvm::Error scan(StringRef Directory);

/// Take over ownership of stored libraries.
std::vector<Library> takeLibraries() { return std::move(Libraries); };

/// Get all the header files in libraries.
///
/// \param Libraries Reference of collection of libraries.
static HeaderSeq getHeaders(ArrayRef<Library> Libraries);

private:
/// Collect files for dylibs in usr/(local)/lib within directory.
llvm::Error scanForUnwrappedLibraries(StringRef Directory);

/// Collect files for any frameworks within directory.
llvm::Error scanForFrameworks(StringRef Directory);

/// Get a library from the libraries collection.
Library &getOrCreateLibrary(StringRef Path, std::vector<Library> &Libs) const;

/// Collect multiple frameworks from directory.
llvm::Error scanMultipleFrameworks(StringRef Directory,
std::vector<Library> &Libs) const;
/// Collect files from nested frameworks.
llvm::Error scanSubFrameworksDirectory(StringRef Directory,
std::vector<Library> &Libs) const;

/// Collect files from framework path.
llvm::Error scanFrameworkDirectory(StringRef Path, Library &Framework) const;

/// Collect header files from path.
llvm::Error scanHeaders(StringRef Path, Library &Lib, HeaderType Type,
StringRef BasePath,
StringRef ParentPath = StringRef()) const;

/// Collect files from Version directories inside Framework directories.
llvm::Error scanFrameworkVersionsDirectory(StringRef Path,
Library &Lib) const;
FileManager &FM;
ScanMode Mode;
StringRef RootPath;
std::vector<Library> Libraries;
};

} // namespace clang::installapi

#endif // LLVM_CLANG_INSTALLAPI_DIRECTORYSCANNER_H
13 changes: 13 additions & 0 deletions clang/include/clang/InstallAPI/HeaderFile.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,19 @@ class HeaderFile {
Other.Excluded, Other.Extra,
Other.Umbrella);
}

bool operator<(const HeaderFile &Other) const {
/// For parsing of headers based on ordering,
/// group by type, then whether its an umbrella.
/// Capture 'extra' headers last.
/// This optimizes the chance of a sucessful parse for
/// headers that violate IWYU.
if (isExtra() && Other.isExtra())
return std::tie(Type, Umbrella) < std::tie(Other.Type, Other.Umbrella);

return std::tie(Type, Umbrella, Extra, FullPath) <
std::tie(Other.Type, Other.Umbrella, Other.Extra, Other.FullPath);
}
};

/// Glob that represents a pattern of header files to retreive.
Expand Down
65 changes: 65 additions & 0 deletions clang/include/clang/InstallAPI/Library.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
//===- InstallAPI/Library.h -------------------------------------*- C++ -*-===//
//
// 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
//
//===----------------------------------------------------------------------===//
///
/// Defines the content of a library, such as public and private
/// header files, and whether it is a framework.
///
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_INSTALLAPI_LIBRARY_H
#define LLVM_CLANG_INSTALLAPI_LIBRARY_H

#include "clang/InstallAPI/HeaderFile.h"
#include "clang/InstallAPI/MachO.h"

namespace clang::installapi {

class Library {
public:
Library(StringRef Directory) : BaseDirectory(Directory) {}

/// Capture the name of the framework by the install name.
///
/// \param InstallName The install name of the library encoded in a dynamic
/// library.
static StringRef getFrameworkNameFromInstallName(StringRef InstallName);

/// Get name of library by the discovered file path.
StringRef getName() const;

/// Get discovered path of library.
StringRef getPath() const { return BaseDirectory; }

/// Add a header file that belongs to the library.
///
/// \param FullPath Path to header file.
/// \param Type Access level of header.
/// \param IncludePath The way the header should be included.
void addHeaderFile(StringRef FullPath, HeaderType Type,
StringRef IncludePath = StringRef()) {
Headers.emplace_back(FullPath, Type, IncludePath);
}

/// Determine if library is empty.
bool empty() {
return SubFrameworks.empty() && Headers.empty() &&
FrameworkVersions.empty();
}

private:
std::string BaseDirectory;
HeaderSeq Headers;
std::vector<Library> SubFrameworks;
std::vector<Library> FrameworkVersions;
bool IsUnwrappedDylib{false};

friend class DirectoryScanner;
};

} // namespace clang::installapi

#endif // LLVM_CLANG_INSTALLAPI_LIBRARY_H
1 change: 1 addition & 0 deletions clang/include/clang/InstallAPI/MachO.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ using RecordLinkage = llvm::MachO::RecordLinkage;
using Record = llvm::MachO::Record;
using EncodeKind = llvm::MachO::EncodeKind;
using GlobalRecord = llvm::MachO::GlobalRecord;
using InterfaceFile = llvm::MachO::InterfaceFile;
using ObjCContainerRecord = llvm::MachO::ObjCContainerRecord;
using ObjCInterfaceRecord = llvm::MachO::ObjCInterfaceRecord;
using ObjCCategoryRecord = llvm::MachO::ObjCCategoryRecord;
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/InstallAPI/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@ set(LLVM_LINK_COMPONENTS

add_clang_library(clangInstallAPI
DiagnosticBuilderWrappers.cpp
DirectoryScanner.cpp
DylibVerifier.cpp
FileList.cpp
Frontend.cpp
HeaderFile.cpp
Library.cpp
Visitor.cpp

LINK_LIBS
Expand Down
Loading

0 comments on commit feed66f

Please sign in to comment.