Skip to content

Reapply "[InstallAPI] Add --extra* and --exclude* cli options for header input (#86522)" #86574

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions clang/include/clang/Basic/DiagnosticInstallAPIKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ let CategoryName = "Command line" in {
def err_cannot_write_file : Error<"cannot write file '%0': %1">;
def err_no_install_name : Error<"no install name specified: add -install_name <path>">;
def err_no_output_file: Error<"no output file specified">;
def err_no_such_header_file : Error<"no such %select{public|private|project}1 header file: '%0'">;
def warn_no_such_excluded_header_file : Warning<"no such excluded %select{public|private}0 header file: '%1'">, InGroup<InstallAPIViolation>;
def warn_glob_did_not_match: Warning<"glob '%0' did not match any header file">, InGroup<InstallAPIViolation>;
} // end of command line category.

let CategoryName = "Verification" in {
Expand Down
54 changes: 52 additions & 2 deletions clang/include/clang/InstallAPI/HeaderFile.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@
#ifndef LLVM_CLANG_INSTALLAPI_HEADERFILE_H
#define LLVM_CLANG_INSTALLAPI_HEADERFILE_H

#include "clang/Basic/FileManager.h"
#include "clang/Basic/LangStandard.h"
#include "clang/InstallAPI/MachO.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/Regex.h"
Expand Down Expand Up @@ -56,6 +58,10 @@ class HeaderFile {
std::string IncludeName;
/// Supported language mode for header.
std::optional<clang::Language> Language;
/// Exclude header file from processing.
bool Excluded{false};
/// Add header file to processing.
bool Extra{false};

public:
HeaderFile() = delete;
Expand All @@ -71,17 +77,48 @@ class HeaderFile {
StringRef getIncludeName() const { return IncludeName; }
StringRef getPath() const { return FullPath; }

void setExtra(bool V = true) { Extra = V; }
void setExcluded(bool V = true) { Excluded = V; }
bool isExtra() const { return Extra; }
bool isExcluded() const { return Excluded; }

bool useIncludeName() const {
return Type != HeaderType::Project && !IncludeName.empty();
}

bool operator==(const HeaderFile &Other) const {
return std::tie(Type, FullPath, IncludeName, Language) ==
return std::tie(Type, FullPath, IncludeName, Language, Excluded, Extra) ==
std::tie(Other.Type, Other.FullPath, Other.IncludeName,
Other.Language);
Other.Language, Other.Excluded, Other.Extra);
}
};

/// Glob that represents a pattern of header files to retreive.
class HeaderGlob {
private:
std::string GlobString;
llvm::Regex Rule;
HeaderType Type;
bool FoundMatch{false};

public:
HeaderGlob(StringRef GlobString, llvm::Regex &&, HeaderType Type);

/// Create a header glob from string for the header access level.
static llvm::Expected<std::unique_ptr<HeaderGlob>>
create(StringRef GlobString, HeaderType Type);

/// Query if provided header matches glob.
bool match(const HeaderFile &Header);

/// Query if a header was matched in the glob, used primarily for error
/// reporting.
bool didMatch() { return FoundMatch; }

/// Provide back input glob string.
StringRef str() { return GlobString; }
};

/// Assemble expected way header will be included by clients.
/// As in what maps inside the brackets of `#include <IncludeName.h>`
/// For example,
Expand All @@ -93,6 +130,19 @@ class HeaderFile {
std::optional<std::string> createIncludeHeaderName(const StringRef FullPath);
using HeaderSeq = std::vector<HeaderFile>;

/// Determine if Path is a header file.
/// It does not touch the file system.
///
/// \param Path File path to file.
bool isHeaderFile(StringRef Path);

/// Given input directory, collect all header files.
///
/// \param FM FileManager for finding input files.
/// \param Directory Path to directory file.
llvm::Expected<PathSeq> enumerateFiles(clang::FileManager &FM,
StringRef Directory);

} // namespace clang::installapi

#endif // LLVM_CLANG_INSTALLAPI_HEADERFILE_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 @@ -40,6 +40,7 @@ using SymbolSet = llvm::MachO::SymbolSet;
using SimpleSymbol = llvm::MachO::SimpleSymbol;
using FileType = llvm::MachO::FileType;
using PackedVersion = llvm::MachO::PackedVersion;
using PathSeq = llvm::MachO::PathSeq;
using Target = llvm::MachO::Target;
using TargetList = llvm::MachO::TargetList;

Expand Down
2 changes: 2 additions & 0 deletions clang/lib/InstallAPI/Frontend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,8 @@ std::unique_ptr<MemoryBuffer> createInputBuffer(InstallAPIContext &Ctx) {
SmallString<4096> Contents;
raw_svector_ostream OS(Contents);
for (const HeaderFile &H : Ctx.InputHeaders) {
if (H.isExcluded())
continue;
if (H.getType() != Ctx.Type)
continue;
if (Ctx.LangMode == Language::C || Ctx.LangMode == Language::CXX)
Expand Down
51 changes: 51 additions & 0 deletions clang/lib/InstallAPI/HeaderFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//

#include "clang/InstallAPI/HeaderFile.h"
#include "llvm/TextAPI/Utils.h"

using namespace llvm;
namespace clang::installapi {
Expand Down Expand Up @@ -34,4 +35,54 @@ std::optional<std::string> createIncludeHeaderName(const StringRef FullPath) {
return Matches[1].drop_front(Matches[1].rfind('/') + 1).str() + "/" +
Matches[3].str();
}

bool isHeaderFile(StringRef Path) {
return StringSwitch<bool>(sys::path::extension(Path))
.Cases(".h", ".H", ".hh", ".hpp", ".hxx", true)
.Default(false);
}

llvm::Expected<PathSeq> enumerateFiles(FileManager &FM, StringRef Directory) {
PathSeq Files;
std::error_code EC;
auto &FS = FM.getVirtualFileSystem();
for (llvm::vfs::recursive_directory_iterator i(FS, Directory, EC), ie;
i != ie; i.increment(EC)) {
if (EC)
return errorCodeToError(EC);

// Skip files that do not exist. This usually happens for broken symlinks.
if (FS.status(i->path()) == std::errc::no_such_file_or_directory)
continue;

StringRef Path = i->path();
if (isHeaderFile(Path))
Files.emplace_back(Path);
}

return Files;
}

HeaderGlob::HeaderGlob(StringRef GlobString, Regex &&Rule, HeaderType Type)
: GlobString(GlobString), Rule(std::move(Rule)), Type(Type) {}

bool HeaderGlob::match(const HeaderFile &Header) {
if (Header.getType() != Type)
return false;

bool Match = Rule.match(Header.getPath());
if (Match)
FoundMatch = true;
return Match;
}

Expected<std::unique_ptr<HeaderGlob>> HeaderGlob::create(StringRef GlobString,
HeaderType Type) {
auto Rule = MachO::createRegexFromGlob(GlobString);
if (!Rule)
return Rule.takeError();

return std::make_unique<HeaderGlob>(GlobString, std::move(*Rule), Type);
}

} // namespace clang::installapi
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
extern int extraGlobalAPI1;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
extern int extraGlobalAPI2;
103 changes: 103 additions & 0 deletions clang/test/InstallAPI/Inputs/Simple/Simple.framework/Headers/Basic.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
#import <Foundation/Foundation.h>

// Basic class with no super class
@interface Basic1
@end

@interface Basic2 : NSObject
@end

@interface Basic3 : NSObject
@property BOOL property1;
@property(readonly) BOOL property2;
@property(getter=isProperty3) BOOL property3;
@property BOOL dynamicProp;
@end

@interface Basic4 : NSObject {
@public
BOOL ivar1;
@protected
BOOL ivar2;
@package
BOOL ivar3;
@private
BOOL ivar4;
}
@end

__attribute__((visibility("hidden"))) @interface Basic4_1 : NSObject {
@public
BOOL ivar1;
@protected
BOOL ivar2;
@package
BOOL ivar3;
@private
BOOL ivar4;
}
@end

@interface Basic4_2 : NSObject {
@private
BOOL ivar4;
@package
BOOL ivar3;
@protected
BOOL ivar2;
@public
BOOL ivar1;
}
@end

@interface Basic5 : NSObject
+ (void)aClassMethod;
- (void)anInstanceMethod;
@end

@interface Basic6 : NSObject
@end

@interface Basic6 () {
@public
BOOL ivar1;
}
@property BOOL property1;
- (void)anInstanceMethodFromAnExtension;
@end

@interface Basic6 (Foo)
@property BOOL property2;
- (void)anInstanceMethodFromACategory;
@end

__attribute__((visibility("hidden")))
@interface Basic7 : NSObject
@end

@interface Basic7 ()
- (void) anInstanceMethodFromAnHiddenExtension;
@end

@interface Basic8 : NSObject
+ (void)useSameName;
@end

// Classes and protocols can have the same name. For now they would only clash
// in the selector map if the protocl starts with '_'.
@protocol _A
- (void)aMethod;
@end

@interface A : NSObject
- (void)aMethod NS_AVAILABLE(10_11, 9_0);
- (void)bMethod NS_UNAVAILABLE;
@end

@interface Basic9 : NSObject
@property(readonly) BOOL aProperty NS_AVAILABLE(10_10, 8_0);
@end

@interface Basic9 (deprecated)
@property(readwrite) BOOL aProperty NS_DEPRECATED_MAC(10_8, 10_10);
@end
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#import <Foundation/Foundation.h>

// Sub-class an external defined ObjC Class.
@interface ExternalManagedObject : NSManagedObject
- (void)foo;
@end

// Add category to external defined ObjC Class.
@interface NSManagedObject (Simple)
- (int)supportsSimple;
@end

// CoreData Accessors are dynamically generated and have no implementation.
@interface ExternalManagedObject (CoreDataGeneratedAccessors)
- (void)addChildObject:(ExternalManagedObject *)value;
- (void)removeChildObject:(ExternalManagedObject *)value;
- (void)addChild:(NSSet *)values;
- (void)removeChild:(NSSet *)values;
@end
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#import <Foundation/Foundation.h>

// Useless forward declaration. This is used for testing.
@class FooBar;
@protocol FooProtocol;

@protocol ForwardProcotol;

// Test public global.
extern int publicGlobalVariable;

// Test weak public global.
extern int weakPublicGlobalVariable __attribute__((weak));

// Test public ObjC class
@interface Simple : NSObject
@end

__attribute__((objc_exception))
@interface Base : NSObject
@end

@interface SubClass : Base
@end

@protocol BaseProtocol
- (void) baseMethod;
@end

NS_AVAILABLE(10_11, 9_0)
@protocol FooProtocol <BaseProtocol>
- (void) protocolMethod;
@end

@protocol BarProtocol
- (void) barMethod;
@end

@interface FooClass <FooProtocol, BarProtocol>
@end

// Create an empty category conforms to a forward declared protocol.
// <rdar://problem/35605892>
@interface FooClass (Test) <ForwardProcotol>
@end
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
extern int otherFrameworkAPI;
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Test private global variable.
extern int privateGlobalVariable;

// Test weak private global.
extern int weakPrivateGlobalVariable __attribute__((weak));
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// Test private global variable.
extern int otherFrameworkSPI;
Loading