diff --git a/examples/pkg_manifest_minimal/Package.resolved b/examples/pkg_manifest_minimal/Package.resolved index fd38c1a23..a9b8d59cb 100644 --- a/examples/pkg_manifest_minimal/Package.resolved +++ b/examples/pkg_manifest_minimal/Package.resolved @@ -5,8 +5,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-argument-parser", "state" : { - "revision" : "c8ed701b513cf5177118a175d85fbbbcd707ab41", - "version" : "1.3.0" + "revision" : "41982a3656a71c768319979febd796c6fd111d5c", + "version" : "1.5.0" } }, { @@ -14,8 +14,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-log", "state" : { - "revision" : "e97a6fcb1ab07462881ac165fdbb37f067e205d5", - "version" : "1.5.4" + "revision" : "96a2f8a0fa41e9e09af4585e2724c4e825410b91", + "version" : "1.6.2" } }, { @@ -23,8 +23,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/nicklockwood/SwiftFormat", "state" : { - "revision" : "607c7057e55cf008e3841696ea7083622e36164f", - "version" : "0.53.2" + "revision" : "468a7d32dedc8d352c191594b3b45d9fd8ba291b", + "version" : "0.55.5" } } ], diff --git a/examples/pkg_manifest_minimal/Sources/MyExecutable/BUILD.bazel b/examples/pkg_manifest_minimal/Sources/MyExecutable/BUILD.bazel index 0a2bf86dc..c71a171da 100644 --- a/examples/pkg_manifest_minimal/Sources/MyExecutable/BUILD.bazel +++ b/examples/pkg_manifest_minimal/Sources/MyExecutable/BUILD.bazel @@ -7,6 +7,7 @@ swift_binary( visibility = ["//visibility:public"], deps = [ "//Sources/MyLibrary", + "@swiftpkg_my_local_package//:FarewellFramework", "@swiftpkg_my_local_package//:GreetingsFramework", "@swiftpkg_notthatamazingmodule//:NotThatAmazingModule", "@swiftpkg_swift_argument_parser//:ArgumentParser", diff --git a/examples/pkg_manifest_minimal/Sources/MyExecutable/MyExecutable.swift b/examples/pkg_manifest_minimal/Sources/MyExecutable/MyExecutable.swift index 36a075e06..7be2014f7 100644 --- a/examples/pkg_manifest_minimal/Sources/MyExecutable/MyExecutable.swift +++ b/examples/pkg_manifest_minimal/Sources/MyExecutable/MyExecutable.swift @@ -1,4 +1,5 @@ import ArgumentParser +import FarewellFramework import GreetingsFramework import MyLibrary import NotThatAmazingModule @@ -10,6 +11,8 @@ struct MyExecutable: AsyncParsableCommand { print(namedGreeting.value) let complexClass = ComplexClass(name: "Olivia", age: 30, favoriteColors: ["blue"]) - print(complexClass.greet()) + complexClass.greet() + let farewellMessage = FarewellFramework.myclang_get_farewell_message(MYCLANG_FAREWELL_GOODBYE) + print(String(cString: farewellMessage!)) } } diff --git a/examples/pkg_manifest_minimal/do_test b/examples/pkg_manifest_minimal/do_test index 75c2894bb..1d6c3b114 100755 --- a/examples/pkg_manifest_minimal/do_test +++ b/examples/pkg_manifest_minimal/do_test @@ -25,6 +25,7 @@ assert_match() { output="$("${bazel}" run //Sources/MyExecutable)" assert_match "Good morning, World!" "${output}" assert_match "Olivia .* 30 years old" "${output}" +assert_match "Goodbye" "${output}" # Run old-style executable in my_local_package output="$("${bazel}" run @swiftpkg_my_local_package//:print-greeting)" diff --git a/examples/pkg_manifest_minimal/res_log.yml b/examples/pkg_manifest_minimal/res_log.yml new file mode 100644 index 000000000..a3cd3b405 --- /dev/null +++ b/examples/pkg_manifest_minimal/res_log.yml @@ -0,0 +1,72 @@ +--- +name: //Sources/MyExecutable +kind: swift_binary +imports: + - ArgumentParser + - FarewellFramework + - GreetingsFramework + - MyLibrary + - NotThatAmazingModule +local_resolution: + - module: MyLibrary + label: //Sources/MyLibrary +external_resolution: + modules: + - ArgumentParser + - FarewellFramework + - GreetingsFramework + - NotThatAmazingModule + products: + - identity: my_local_package + name: FarewellFramework + label: '@swiftpkg_my_local_package//:FarewellFramework' + - identity: my_local_package + name: GreetingsFramework + label: '@swiftpkg_my_local_package//:GreetingsFramework' + - identity: notthatamazingmodule + name: NotThatAmazingModule + label: '@swiftpkg_notthatamazingmodule//:NotThatAmazingModule' + - identity: swift-argument-parser + name: ArgumentParser + label: '@swiftpkg_swift_argument_parser//:ArgumentParser' + unresolved: [] +deps: + - //Sources/MyLibrary + - '@swiftpkg_my_local_package//:FarewellFramework' + - '@swiftpkg_my_local_package//:GreetingsFramework' + - '@swiftpkg_notthatamazingmodule//:NotThatAmazingModule' + - '@swiftpkg_swift_argument_parser//:ArgumentParser' +--- +name: //Sources/MyLibrary +kind: swift_library +imports: + - GreetingsFramework +external_resolution: + modules: + - GreetingsFramework + products: + - identity: my_local_package + name: GreetingsFramework + label: '@swiftpkg_my_local_package//:GreetingsFramework' + unresolved: [] +deps: + - '@swiftpkg_my_local_package//:GreetingsFramework' +--- +name: //Tests/MyLibraryTests +kind: swift_test +imports: + - MyLibrary + - XCTest +builtins: + - XCTest +local_resolution: + - module: MyLibrary + label: //Sources/MyLibrary +external_resolution: + modules: + - XCTest + products: [] + unresolved: + - XCTest +deps: + - //Sources/MyLibrary diff --git a/examples/pkg_manifest_minimal/third_party/my_local_package/Package.swift b/examples/pkg_manifest_minimal/third_party/my_local_package/Package.swift index 31eec3289..49eb1433d 100644 --- a/examples/pkg_manifest_minimal/third_party/my_local_package/Package.swift +++ b/examples/pkg_manifest_minimal/third_party/my_local_package/Package.swift @@ -6,22 +6,35 @@ let package = Package( name: "MyLocalPackage", products: [ .executable(name: "print-greeting", targets: ["PrintGreeting"]), + .executable(name: "print-farewell", targets: ["PrintFarewell"]), .library( name: "GreetingsFramework", targets: ["GreetingsFramework"] ), + .library( + name: "FarewellFramework", + targets: ["FarewellFramework"] + ), ], targets: [ // Puposefully, using the old-style pattern of a regular target being used by an executable // product. - .target( + .executableTarget( name: "PrintGreeting", dependencies: ["GreetingsFramework"] ), + .executableTarget( + name: "PrintFarewell", + dependencies: ["FarewellFramework"] + ), .target( name: "GreetingsFramework", dependencies: [] ), + .target( + name: "FarewellFramework", + dependencies: [] + ), .testTarget( name: "GreetingsFrameworkTests", dependencies: ["GreetingsFramework"] diff --git a/examples/pkg_manifest_minimal/third_party/my_local_package/Sources/FarewellFramework/include/MyClangLibrary/API.h b/examples/pkg_manifest_minimal/third_party/my_local_package/Sources/FarewellFramework/include/MyClangLibrary/API.h new file mode 100644 index 000000000..8eb9c7d1a --- /dev/null +++ b/examples/pkg_manifest_minimal/third_party/my_local_package/Sources/FarewellFramework/include/MyClangLibrary/API.h @@ -0,0 +1,62 @@ +#ifndef MYCLANG_LIBRARY_API_H +#define MYCLANG_LIBRARY_API_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Version information */ +#define MYCLANG_VERSION_MAJOR 1 +#define MYCLANG_VERSION_MINOR 0 +#define MYCLANG_VERSION_PATCH 0 + +/* Maximum buffer sizes */ +#define MYCLANG_MAX_NAME_LENGTH 256 +#define MYCLANG_MAX_BUFFER_SIZE 4096 + +/* Status codes */ +typedef enum { + MYCLANG_SUCCESS = 0, + MYCLANG_ERROR_INVALID_ARGUMENT = -1, + MYCLANG_ERROR_BUFFER_OVERFLOW = -2, + MYCLANG_ERROR_NOT_INITIALIZED = -3 +} MyclangStatus; + +/* Farewell message types */ +typedef enum { + MYCLANG_FAREWELL_GOODBYE = 0, + MYCLANG_FAREWELL_PARTING = 1, + MYCLANG_FAREWELL_SEE_YOU_LATER = 2, + MYCLANG_FAREWELL_TAKE_CARE = 3, + MYCLANG_FAREWELL_ADIEU = 4, + MYCLANG_FAREWELL_DEPARTURE = 5 +} MyclangFarewellType; + +/* Data structures */ +typedef struct { + char name[MYCLANG_MAX_NAME_LENGTH]; + unsigned int id; + double value; +} MyclangObject; + +/* Function declarations */ +MyclangStatus myclang_initialize(void); +MyclangStatus myclang_cleanup(void); + +MyclangStatus myclang_create_object(MyclangObject* obj, + const char* name, + unsigned int id, + double value); + +MyclangStatus myclang_process_object(const MyclangObject* obj); + +const char* myclang_get_version_string(void); + +/* Farewell function */ +const char* myclang_get_farewell_message(MyclangFarewellType type); + +#ifdef __cplusplus +} +#endif + +#endif /* MYCLANG_LIBRARY_API_H */ diff --git a/examples/pkg_manifest_minimal/third_party/my_local_package/Sources/FarewellFramework/src/lib.c b/examples/pkg_manifest_minimal/third_party/my_local_package/Sources/FarewellFramework/src/lib.c new file mode 100644 index 000000000..a9f682f16 --- /dev/null +++ b/examples/pkg_manifest_minimal/third_party/my_local_package/Sources/FarewellFramework/src/lib.c @@ -0,0 +1,86 @@ +#include +#include +#include "MyClangLibrary/API.h" + +static int g_initialized = 0; + +MyclangStatus myclang_initialize(void) { + if (g_initialized) { + return MYCLANG_ERROR_INVALID_ARGUMENT; + } + g_initialized = 1; + return MYCLANG_SUCCESS; +} + +MyclangStatus myclang_cleanup(void) { + if (!g_initialized) { + return MYCLANG_ERROR_NOT_INITIALIZED; + } + g_initialized = 0; + return MYCLANG_SUCCESS; +} + +MyclangStatus myclang_create_object(MyclangObject* obj, + const char* name, + unsigned int id, + double value) { + if (!g_initialized) { + return MYCLANG_ERROR_NOT_INITIALIZED; + } + + if (!obj || !name) { + return MYCLANG_ERROR_INVALID_ARGUMENT; + } + + if (strlen(name) >= MYCLANG_MAX_NAME_LENGTH) { + return MYCLANG_ERROR_BUFFER_OVERFLOW; + } + + strncpy(obj->name, name, MYCLANG_MAX_NAME_LENGTH - 1); + obj->name[MYCLANG_MAX_NAME_LENGTH - 1] = '\0'; + obj->id = id; + obj->value = value; + + return MYCLANG_SUCCESS; +} + +MyclangStatus myclang_process_object(const MyclangObject* obj) { + if (!g_initialized) { + return MYCLANG_ERROR_NOT_INITIALIZED; + } + + if (!obj) { + return MYCLANG_ERROR_INVALID_ARGUMENT; + } + + // Example processing - in real implementation, this would do something useful + return MYCLANG_SUCCESS; +} + +const char* myclang_get_version_string(void) { + static char version[32]; + snprintf(version, sizeof(version), "%d.%d.%d", + MYCLANG_VERSION_MAJOR, + MYCLANG_VERSION_MINOR, + MYCLANG_VERSION_PATCH); + return version; +} + +const char* myclang_get_farewell_message(MyclangFarewellType type) { + switch (type) { + case MYCLANG_FAREWELL_GOODBYE: + return "Goodbye"; + case MYCLANG_FAREWELL_PARTING: + return "Parting"; + case MYCLANG_FAREWELL_SEE_YOU_LATER: + return "See you later"; + case MYCLANG_FAREWELL_TAKE_CARE: + return "Take care"; + case MYCLANG_FAREWELL_ADIEU: + return "Adieu"; + case MYCLANG_FAREWELL_DEPARTURE: + return "Departure"; + default: + return "Goodbye"; + } +} diff --git a/examples/pkg_manifest_minimal/third_party/my_local_package/Sources/PrintFarewell/main.swift b/examples/pkg_manifest_minimal/third_party/my_local_package/Sources/PrintFarewell/main.swift new file mode 100644 index 000000000..26aedace5 --- /dev/null +++ b/examples/pkg_manifest_minimal/third_party/my_local_package/Sources/PrintFarewell/main.swift @@ -0,0 +1,7 @@ +import FarewellFramework + +let message = FarewellFramework.myclang_get_farewell_message(MYCLANG_FAREWELL_SEE_YOU_LATER) + +let swiftString = String(cString: message!) + +print(swiftString) diff --git a/swiftpkg/internal/clang_files.bzl b/swiftpkg/internal/clang_files.bzl index 1fe0790c1..32d5d21d5 100644 --- a/swiftpkg/internal/clang_files.bzl +++ b/swiftpkg/internal/clang_files.bzl @@ -118,7 +118,7 @@ def _is_public_modulemap(path, public_includes = []): return False -def _get_hdr_paths_from_modulemap(repository_ctx, modulemap_path, module_name): +def _get_hdr_paths_from_modulemap(repository_ctx, modulemap_path): """Retrieves the list of headers declared in the specified modulemap file \ for the specified module. @@ -128,7 +128,6 @@ def _get_hdr_paths_from_modulemap(repository_ctx, modulemap_path, module_name): Args: repository_ctx: A `repository_ctx` instance. modulemap_path: A path `string` to the `module.modulemap` file. - module_name: The name of the module. Returns: A `list` of path `string` values. @@ -142,23 +141,21 @@ def _get_hdr_paths_from_modulemap(repository_ctx, modulemap_path, module_name): if len(module_decls) == 0: fail("No module declarations were found in %s." % (modulemap_path)) - # Look for a module declaration that matches the module name. Only select - # headers from that module if it is found. Otherwise, we collect all of the - # headers in all of the module declarations at the top-level. - module_decl = lists.find(module_decls, lambda m: m.module_id == module_name) - if module_decl != None: - module_decls = [module_decl] - modulemap_dirname = paths.dirname(modulemap_path) hdrs = [] for module_decl in module_decls: for cdecl in module_decl.members: + if cdecl.decl_type == dts.umbrella_header: + # If the module has an umbrella header, then it is the only public header. + # All other headers are private. + umbrella_hdr = cdecl.path + normalized_umbrella_hdr = paths.normalize(paths.join(modulemap_dirname, umbrella_hdr)) + hdrs.append(normalized_umbrella_hdr) if cdecl.decl_type == dts.single_header and not cdecl.private and not cdecl.textual: # Resolve the path relative to the modulemap hdr_path = paths.join(modulemap_dirname, cdecl.path) normalized_hdr_path = paths.normalize(hdr_path) hdrs.append(normalized_hdr_path) - return hdrs def _is_under_path(path, parent): @@ -264,11 +261,6 @@ def _collect_files( orig_path, public_includes = public_includes, ): - if modulemap != None: - fail("Found multiple modulemap files. {first} {second}".format( - first = modulemap, - second = path, - )) modulemap_orig_path = orig_path modulemap = path else: @@ -280,10 +272,13 @@ def _collect_files( # the referenced headers are included. For now, we will just add the # modulemap hdrs to the ones that we have already found. if modulemap_orig_path != None: + # If Swift Package Library provides a modulemap file in the include + # directory, then we should include all of the headers that are listed + # in the modulemap file. + mm_hdrs = _get_hdr_paths_from_modulemap( repository_ctx, - modulemap_orig_path, - module_name, + modulemap_orig_path ) mm_hdrs = _relativize_paths(mm_hdrs, relative_to) @@ -297,7 +292,10 @@ def _collect_files( ]) mm_hdrs_set = sets.make(mm_hdrs) - hdrs_set = sets.union(hdrs_set, mm_hdrs_set) + if len(mm_hdrs) > 0: + # If we have found public headers in the modulemap, then we should set them as the public headers. + srcs_set = sets.union(srcs_set, hdrs_set) + hdrs_set = mm_hdrs_set # If we have not found any public header files for a library module, then # promote any headers that are listed in the srcs. @@ -308,14 +306,13 @@ def _collect_files( sets.insert(hdrs_set, src) srcs_set = sets.difference(srcs_set, hdrs_set) - # If public includes were specified, then use them. Otherwise, add every - # directory that holds a public header file and add any magical public - # header directories that we find. + # If public includes were specified, then use them. + # Otherwise, add any magical public header directories that we find. if len(public_includes) == 0: - public_includes = [paths.dirname(hdr) for hdr in sets.to_list(hdrs_set)] + public_header_folders = ["{}/".format(paths.dirname(hdr)) for hdr in sets.to_list(hdrs_set)] magical_public_hdr_dirs = [] - for pi in public_includes: - magical_public_hdr_dir = clang_files.find_magical_public_hdr_dir(pi) + for public_header_folder in public_header_folders: + magical_public_hdr_dir = _find_magical_public_hdr_dir(public_header_folder) if magical_public_hdr_dir != None: magical_public_hdr_dirs.append(magical_public_hdr_dir) public_includes.extend(magical_public_hdr_dirs) diff --git a/swiftpkg/internal/pkginfos.bzl b/swiftpkg/internal/pkginfos.bzl index ab15f134b..dbeff39c4 100644 --- a/swiftpkg/internal/pkginfos.bzl +++ b/swiftpkg/internal/pkginfos.bzl @@ -375,6 +375,17 @@ def _new_target_from_json_maps( pkg_path = pkg_path, sources = clang_src_info.explicit_srcs + clang_src_info.hdrs, ) + elif module_type == module_types.system_library: + clang_src_info = _new_clang_src_info_from_sources( + repository_ctx = repository_ctx, + pkg_path = pkg_path, + c99name = c99name, + target_path = target_path, + source_paths = source_paths, + public_hdrs_path = "", # System libraries have their headers/modulemaps in the library root path, so the root path is the public path + exclude_paths = exclude_paths, + other_hdr_srch_paths = [], + ) return _new_target( name = target_name, @@ -1072,11 +1083,28 @@ def _new_clang_src_info_from_sources( paths.join(pkg_path, target_path), ) + # If the Swift package manifest does not specify a public headers path, + # use the default "include" directory, if it exists. + # This copies the behavior of the canonical Swift Package Manager implementation. + # https://developer.apple.com/documentation/packagedescription/target/publicheaderspath public_includes = [] if public_hdrs_path != None: public_includes.append( paths.normalize(paths.join(abs_target_path, public_hdrs_path)), ) + elif repository_files.path_exists( + repository_ctx, + paths.join(abs_target_path, "include"), + ): + public_includes.append(paths.join(abs_target_path, "include")) + + # If the Swift package manifest does not specify a public headers path, + # use the default "include" directory, if it exists. + # This copies the behavior of the canonical Swift Package Manager implementation. + # https://developer.apple.com/documentation/packagedescription/target/publicheaderspath + if public_hdrs_path == None: + if repository_files.path_exists(repository_ctx, paths.join(abs_target_path, "include")): + public_includes.append(paths.join(abs_target_path, "include")) # If the Swift package manifest has explicit source paths, respect them. # (Be sure to include any explicitly specified include directories.) diff --git a/swiftpkg/internal/swiftpkg_build_files.bzl b/swiftpkg/internal/swiftpkg_build_files.bzl index 3d5b8ad4f..2e12e0ca7 100644 --- a/swiftpkg/internal/swiftpkg_build_files.bzl +++ b/swiftpkg/internal/swiftpkg_build_files.bzl @@ -19,7 +19,7 @@ def _new_for_target(repository_ctx, pkg_ctx, target, artifact_infos = []): if target.module_type == module_types.clang: return _clang_target_build_file(repository_ctx, pkg_ctx, target) elif target.module_type == module_types.swift: - return _swift_target_build_file(pkg_ctx, target) + return _swift_target_build_file(repository_ctx, pkg_ctx, target) elif target.module_type == module_types.system_library: return _system_library_build_file(target) elif target.module_type == module_types.binary: @@ -36,7 +36,7 @@ def _new_for_target(repository_ctx, pkg_ctx, target, artifact_infos = []): # MARK: - Swift Target -def _swift_target_build_file(pkg_ctx, target): +def _swift_target_build_file(repository_ctx, pkg_ctx, target): if target.swift_src_info == None: fail("Expected a `swift_src_info`. name: ", target.name) @@ -80,10 +80,12 @@ def _swift_target_build_file(pkg_ctx, target): fail_if_not_found = False, ) - if not dep_target or dep_target.type != target_types.macro: + if dep_target and dep_target.type == target_types.macro: + macro_targets.append(dep_target) + else: target_deps.append(target_dep) continue - macro_targets.append(dep_target) + if macro_targets: attrs["plugins"] = [ @@ -111,6 +113,15 @@ def _swift_target_build_file(pkg_ctx, target): "-DSWIFT_PACKAGE", ] + linkopts = [] + if target.linker_settings != None: + linkopts.extend(lists.flatten([ + bzl_selects.new_from_build_setting(bs) + for bs in target.linker_settings.linked_libraries + ])) + if linkopts: + attrs["linkopts"] = _starlarkify_clang_attrs(repository_ctx, {"linkopts": linkopts})["linkopts"] + # GH046: Support plugins. is_library_target = lists.contains([target_types.library, target_types.regular], target.type) @@ -651,12 +662,49 @@ def _starlarkify_clang_attrs(repository_ctx, attrs): # MARK: - System Library Targets -# GH009(chuck): Remove unused-variable directives - -# buildifier: disable=unused-variable def _system_library_build_file(target): - # GH009(chuck): Implement _system_library_build_file - return None + attrs = { + "visibility": ["//:__subpackages__"], + } + + # These flags are used by SPM when compiling clang modules. + copts = [ + # Enable 'blocks' language feature + "-fblocks", + # Synthesize retain and release calls for Objective-C pointers + "-fobjc-arc", + # Enable support for PIC macros + "-fPIC", + # The SWIFT_PACKAGE define is a magical value that SPM uses when it + # builds clang libraries that will be used as Swift modules. + "-DSWIFT_PACKAGE=1", + ] + attrs["copts"] = copts + + module_map_file = target.clang_src_info.modulemap_path + attrs["module_map"] = module_map_file + + # System library targets must include a modulemap file. + # https://github.com/swiftlang/swift-package-manager/blob/12c14222fdde2ffd8303a2c805fed1b1eb802e5c/Sources/PackageLoading/PackageBuilder.swift#L853 + if not module_map_file: + fail("Expected a modulemap file for a system library target. name: ", target.name) + + header_files = target.clang_src_info.hdrs + attrs["hdrs"] = header_files + + bzl_target_name = pkginfo_targets.bazel_label_name(target) + + decls = [ + build_decls.new( + kind = objc_kinds.library, + name = bzl_target_name, + attrs = attrs, + ) + ] + + return build_files.new( + decls = decls, + ) # MARK: - Apple xcframework Targets