-
Notifications
You must be signed in to change notification settings - Fork 12k
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
[InstallAPI] Add --extra* and --exclude* cli options for header input #86522
Conversation
InstallAPI takes a json list of headers that is typically generated from a build system like Xcode based on a project's attributes. Sometimes, maintainers may want to alter this for tapi input. Using e.g. `--extra-public-headers`, users can manipulate what headers will be used for TBD file generation.
@llvm/pr-subscribers-clang Author: Cyndy Ishida (cyndyishida) ChangesInstallAPI takes a json list of headers that is typically generated from a build system like Xcode based on a project's attributes. Sometimes, maintainers may want to alter this for tapi input. Using e.g. Patch is 167.20 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/86522.diff 23 Files Affected:
diff --git a/clang/include/clang/Basic/DiagnosticInstallAPIKinds.td b/clang/include/clang/Basic/DiagnosticInstallAPIKinds.td
index a4c6e630ac5fd8..27df731fa28627 100644
--- a/clang/include/clang/Basic/DiagnosticInstallAPIKinds.td
+++ b/clang/include/clang/Basic/DiagnosticInstallAPIKinds.td
@@ -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 {
diff --git a/clang/include/clang/InstallAPI/HeaderFile.h b/clang/include/clang/InstallAPI/HeaderFile.h
index 70e83bbb3e76f6..235b4da3add840 100644
--- a/clang/include/clang/InstallAPI/HeaderFile.h
+++ b/clang/include/clang/InstallAPI/HeaderFile.h
@@ -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"
@@ -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;
@@ -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,
@@ -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
diff --git a/clang/include/clang/InstallAPI/MachO.h b/clang/include/clang/InstallAPI/MachO.h
index f0dea8bbd24ccd..4961c596fd68ae 100644
--- a/clang/include/clang/InstallAPI/MachO.h
+++ b/clang/include/clang/InstallAPI/MachO.h
@@ -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;
diff --git a/clang/lib/InstallAPI/Frontend.cpp b/clang/lib/InstallAPI/Frontend.cpp
index 12cd5fcbc22bf7..e07ccb14e0b80a 100644
--- a/clang/lib/InstallAPI/Frontend.cpp
+++ b/clang/lib/InstallAPI/Frontend.cpp
@@ -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)
diff --git a/clang/lib/InstallAPI/HeaderFile.cpp b/clang/lib/InstallAPI/HeaderFile.cpp
index c2d8372741ee07..0b7041ec8147eb 100644
--- a/clang/lib/InstallAPI/HeaderFile.cpp
+++ b/clang/lib/InstallAPI/HeaderFile.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "clang/InstallAPI/HeaderFile.h"
+#include "llvm/TextAPI/Utils.h"
using namespace llvm;
namespace clang::installapi {
@@ -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
diff --git a/clang/test/InstallAPI/Inputs/Simple/Extra/SimpleExtraAPI1.h b/clang/test/InstallAPI/Inputs/Simple/Extra/SimpleExtraAPI1.h
new file mode 100644
index 00000000000000..83a5b9507de307
--- /dev/null
+++ b/clang/test/InstallAPI/Inputs/Simple/Extra/SimpleExtraAPI1.h
@@ -0,0 +1 @@
+extern int extraGlobalAPI1;
diff --git a/clang/test/InstallAPI/Inputs/Simple/Extra/SimpleExtraAPI2.h b/clang/test/InstallAPI/Inputs/Simple/Extra/SimpleExtraAPI2.h
new file mode 100644
index 00000000000000..34fe3364bba84e
--- /dev/null
+++ b/clang/test/InstallAPI/Inputs/Simple/Extra/SimpleExtraAPI2.h
@@ -0,0 +1 @@
+extern int extraGlobalAPI2;
diff --git a/clang/test/InstallAPI/Inputs/Simple/Simple.framework/Headers/Basic.h b/clang/test/InstallAPI/Inputs/Simple/Simple.framework/Headers/Basic.h
new file mode 100644
index 00000000000000..08412bb2de2838
--- /dev/null
+++ b/clang/test/InstallAPI/Inputs/Simple/Simple.framework/Headers/Basic.h
@@ -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
diff --git a/clang/test/InstallAPI/Inputs/Simple/Simple.framework/Headers/External.h b/clang/test/InstallAPI/Inputs/Simple/Simple.framework/Headers/External.h
new file mode 100644
index 00000000000000..5dc3c92f34c24d
--- /dev/null
+++ b/clang/test/InstallAPI/Inputs/Simple/Simple.framework/Headers/External.h
@@ -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
diff --git a/clang/test/InstallAPI/Inputs/Simple/Simple.framework/Headers/Simple.h b/clang/test/InstallAPI/Inputs/Simple/Simple.framework/Headers/Simple.h
new file mode 100644
index 00000000000000..12c77098a8d9a7
--- /dev/null
+++ b/clang/test/InstallAPI/Inputs/Simple/Simple.framework/Headers/Simple.h
@@ -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
diff --git a/clang/test/InstallAPI/Inputs/Simple/Simple.framework/Headers/SimpleAPI.h b/clang/test/InstallAPI/Inputs/Simple/Simple.framework/Headers/SimpleAPI.h
new file mode 100644
index 00000000000000..d953fac966daf3
--- /dev/null
+++ b/clang/test/InstallAPI/Inputs/Simple/Simple.framework/Headers/SimpleAPI.h
@@ -0,0 +1 @@
+extern int otherFrameworkAPI;
diff --git a/clang/test/InstallAPI/Inputs/Simple/Simple.framework/PrivateHeaders/SimplePrivate.h b/clang/test/InstallAPI/Inputs/Simple/Simple.framework/PrivateHeaders/SimplePrivate.h
new file mode 100644
index 00000000000000..5a28cda3928e3d
--- /dev/null
+++ b/clang/test/InstallAPI/Inputs/Simple/Simple.framework/PrivateHeaders/SimplePrivate.h
@@ -0,0 +1,5 @@
+// Test private global variable.
+extern int privateGlobalVariable;
+
+// Test weak private global.
+extern int weakPrivateGlobalVariable __attribute__((weak));
diff --git a/clang/test/InstallAPI/Inputs/Simple/Simple.framework/PrivateHeaders/SimplePrivateSPI.h b/clang/test/InstallAPI/Inputs/Simple/Simple.framework/PrivateHeaders/SimplePrivateSPI.h
new file mode 100644
index 00000000000000..c9aca30fa82fa8
--- /dev/null
+++ b/clang/test/InstallAPI/Inputs/Simple/Simple.framework/PrivateHeaders/SimplePrivateSPI.h
@@ -0,0 +1,2 @@
+// Test private global variable.
+extern int otherFrameworkSPI;
diff --git a/clang/test/InstallAPI/Inputs/Simple/Simple.yaml b/clang/test/InstallAPI/Inputs/Simple/Simple.yaml
new file mode 100644
index 00000000000000..998e51f1a67dcc
--- /dev/null
+++ b/clang/test/InstallAPI/Inputs/Simple/Simple.yaml
@@ -0,0 +1,3196 @@
+--- !mach-o
+FileHeader:
+ magic: 0xFEEDFACF
+ cputype: 0x1000007
+ cpusubtype: 0x3
+ filetype: 0x6
+ ncmds: 15
+ sizeofcmds: 1952
+ flags: 0x118085
+ reserved: 0x0
+LoadCommands:
+ - cmd: LC_SEGMENT_64
+ cmdsize: 472
+ segname: __TEXT
+ vmaddr: 0
+ vmsize: 12288
+ fileoff: 0
+ filesize: 12288
+ maxprot: 5
+ initprot: 5
+ nsects: 5
+ flags: 0
+ Sections:
+ - sectname: __text
+ segname: __TEXT
+ addr: 0x1BC0
+ size: 180
+ offset: 0x1BC0
+ align: 0
+ reloff: 0x0
+ nreloc: 0
+ flags: 0x80000400
+ reserved1: 0x0
+ reserved2: 0x0
+ reserved3: 0x0
+ content: 554889E50FBE47085DC3554889E58857085DC3554889E50FBE47095DC3554889E50FBE470A5DC3554889E588570A5DC3554889E55DC3554889E55DC3554889E55DC3554889E50FBE47095DC3554889E58857095DC3554889E5B8010000005DC3554889E55DC3554889E55DC3554889E55DC3554889E55DC3554889E5B0015DC3554889E55DC3554889E55DC3554889E55DC3554889E50FBE47085DC3554889E55DC3554889E55DC3554889E55DC3554889E55DC3
+ - sectname: __cstring
+ segname: __TEXT
+ addr: 0x1C74
+ size: 296
+ offset: 0x1C74
+ align: 0
+ reloff: 0x0
+ nreloc: 0
+ flags: 0x2
+ reserved1: 0x0
+ reserved2: 0x0
+ reserved3: 0x0
+ content: 53696D706C65004261736500537562436C6173730053696D706C65496E7465726E616C4150490053696D706C65496E7465726E616C53504900426173696331004261736963320042617369633300426173696334004261736963345F31004261736963345F32004261736963350042617369633600466F6F004261736963370045787465726E616C4D616E616765644F626A6563740048696464656E436C61737300426173696338004100426173696339006465707265636174656400466F6F436C61737300466F6F50726F746F636F6C004261736550726F746F636F6C0042617250726F746F636F6C0050726976617465005072697661746550726F746F636F6C0063313640303A380076323040303A3863313600630076313640303A380042313640303A3800
+ - sectname: __objc_methname
+ segname: __TEXT
+ addr: 0x1D9C
+ size: 450
+ offset: 0x1D9C
+ align: 0
+ reloff: 0x0
+ nreloc: 0
+ flags: 0x2
+ reserved1: 0x0
+ reserved2: 0x0
+ reserved3: 0x0
+ content: 70726F7065727479310073657450726F7065727479313A0070726F70657274793200697350726F7065727479330073657450726F7065727479333A0070726F7065727479330054632C5670726F7065727479310054632C522C5670726F7065727479320054632C47697350726F7065727479332C5670726F7065727479330064796E616D696350726F700054632C440069766172310069766172320069766172330069766172340061436C6173734D6574686F6400616E496E7374616E63654D6574686F6400616E496E7374616E63654D6574686F6446726F6D416E457874656E73696F6E0073657450726F7065727479323A00616E496E7374616E63654D6574686F6446726F6D4143617465676F727900546300616E496E7374616E63654D6574686F6446726F6D416E48696464656E457874656E73696F6E00666F6F00737570706F72747353696D706C650075736553616D654E616D6500614D6574686F64006150726F7065727479005F6150726F70657274790054632C522C565F6150726F706572747900626173654D6574686F640070726F746F636F6C4D6574686F64006261724D6574686F64007072697661746550726F636F746F6C4D6574686F6400
+ - sectname: __unwind_info
+ segname: __TEXT
+ addr: 0x1F60
+ size: 4152
+ offset: 0x1F60
+ align: 2
+ reloff: 0x0
+ nreloc: 0
+ flags: 0x0
+ reserved1: 0x0
+ reserved2: 0x0
+ reserved3: 0x0
+ content: 010000001C000000010000002000000000000000200000000200000000000001C01B00003800000038000000741C00000000000038000000030000000C00010010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000...
[truncated]
|
✅ With the latest revision this PR passed the Python code formatter. |
✅ With the latest revision this PR passed the C/C++ code formatter. |
… for header input (llvm#86522)""
InstallAPI takes a json list of headers that is typically generated from a build system like Xcode based on a project's attributes. Sometimes, maintainers may want to alter this for tapi input. Using e.g.
--extra-public-headers
, users can manipulate what headers will be used for TBD file generation.