From 1f97fac116fde715cc7d1ed85fa11b8de48204e6 Mon Sep 17 00:00:00 2001 From: Kostub D Date: Sun, 7 Jun 2026 17:13:18 +0530 Subject: [PATCH] Fix SPM consumer build: qualify cross-directory header imports (#215) Public headers in render/ imported sibling headers from lib/ by bare filename (e.g. `#import "MTMathList.h"`). That only resolves via the target-internal `lib` header search path, which is applied when building iosMath's own sources but NOT when a downstream SPM consumer builds the `iosMath` Clang module from module.modulemap. Consumers therefore failed with "'MTMathList.h' file not found" / "could not build module 'iosMath'", while the bundled demo and in-package tests worked. Qualify the render -> lib imports as `lib/MTMathList.h` so they resolve from the package's public-headers root (exposed to consumers), and add that root to each target's header search paths so internal builds keep resolving the qualified path. Add `iosMathConsumerTests`, a test target that imports iosMath purely as a Clang module with no header search paths, reproducing external SPM consumption. It fails to compile if a bare cross-directory import returns. Co-Authored-By: Claude Opus 4.8 --- Package.swift | 12 ++++++ iosMath/render/MTFontManager.h | 2 +- iosMath/render/MTMathListDisplay.h | 2 +- iosMath/render/MTMathUILabel.h | 2 +- .../iosMathModuleConsumerTests.swift | 41 +++++++++++++++++++ 5 files changed, 56 insertions(+), 3 deletions(-) create mode 100644 iosMathConsumerTests/iosMathModuleConsumerTests.swift diff --git a/Package.swift b/Package.swift index 105468c..b3b7c55 100644 --- a/Package.swift +++ b/Package.swift @@ -22,6 +22,7 @@ let package = Package( ], publicHeadersPath: ".", cSettings: [ + .headerSearchPath("."), .headerSearchPath("lib"), .headerSearchPath("render"), .headerSearchPath("render/internal"), @@ -33,6 +34,7 @@ let package = Package( path: "iosMathTests", exclude: ["en.lproj"], cSettings: [ + .headerSearchPath("../iosMath"), .headerSearchPath("../iosMath/lib"), .headerSearchPath("../iosMath/render"), .headerSearchPath("../iosMath/render/internal"), @@ -43,6 +45,7 @@ let package = Package( dependencies: ["iosMath"], path: "iosMathSwiftTests", cSettings: [ + .headerSearchPath("../iosMath"), .headerSearchPath("../iosMath/lib"), .headerSearchPath("../iosMath/render"), .headerSearchPath("../iosMath/render/internal"), @@ -51,5 +54,14 @@ let package = Package( .swiftLanguageMode(.v5), ] ), + // Regression guard for issue #215. Imports `iosMath` purely as a Clang + // module with NO header search paths, reproducing how an external SPM + // consumer builds the module. If a public header reintroduces a bare + // cross-directory `#import`, this target fails to compile. + .testTarget( + name: "iosMathConsumerTests", + dependencies: ["iosMath"], + path: "iosMathConsumerTests" + ), ] ) diff --git a/iosMath/render/MTFontManager.h b/iosMath/render/MTFontManager.h index 1292e1f..4e41b80 100644 --- a/iosMath/render/MTFontManager.h +++ b/iosMath/render/MTFontManager.h @@ -13,7 +13,7 @@ @import CoreText; #import "MTFont.h" -#import "MTMathList.h" +#import "lib/MTMathList.h" NS_ASSUME_NONNULL_BEGIN diff --git a/iosMath/render/MTMathListDisplay.h b/iosMath/render/MTMathListDisplay.h index 0232178..7e2fb0d 100644 --- a/iosMath/render/MTMathListDisplay.h +++ b/iosMath/render/MTMathListDisplay.h @@ -18,7 +18,7 @@ #import "MTConfig.h" #import "MTFont.h" -#import "MTMathList.h" +#import "lib/MTMathList.h" NS_ASSUME_NONNULL_BEGIN diff --git a/iosMath/render/MTMathUILabel.h b/iosMath/render/MTMathUILabel.h index a9dff16..2810e09 100644 --- a/iosMath/render/MTMathUILabel.h +++ b/iosMath/render/MTMathUILabel.h @@ -15,7 +15,7 @@ #import "MTConfig.h" #import "MTFont.h" -#import "MTMathList.h" +#import "lib/MTMathList.h" #import "MTMathListDisplay.h" /** diff --git a/iosMathConsumerTests/iosMathModuleConsumerTests.swift b/iosMathConsumerTests/iosMathModuleConsumerTests.swift new file mode 100644 index 0000000..8ddc667 --- /dev/null +++ b/iosMathConsumerTests/iosMathModuleConsumerTests.swift @@ -0,0 +1,41 @@ +// iosMathModuleConsumerTests.swift +// Regression test for issue #215: "Clang dependency scanning failure, +// fatal error: MTMathList.h not found." +// +// This target deliberately depends on `iosMath` and imports it *purely as a +// Clang module*, with NO header search paths configured in Package.swift. That +// reproduces exactly how a downstream Swift Package Manager consumer builds the +// module: from `module.modulemap` against the package's public-headers root, +// without iosMath's target-internal `lib`/`render` search paths. +// +// The bug was that public headers in `render/` imported sibling headers in +// `lib/` by bare filename (e.g. `#import "MTMathList.h"`), which only resolves +// when the internal `lib` search path is present. External consumers (and this +// target) lack that path, so building the module failed with +// "'MTMathList.h' file not found". +// +// If that regression returns, this target FAILS TO COMPILE — `import iosMath` +// forces the module (all module-map headers) to build. The assertions below are +// secondary; the real guard is that this file compiles at all. Referencing a +// `render/` type (MTMathUILabel) and a `lib/` type (MTMathList) together keeps +// the cross-directory include in the compiled surface. + +import XCTest +import iosMath + +final class iosMathModuleConsumerTests: XCTestCase { + + // Exercises the render -> lib cross-directory include path. MTMathUILabel + // lives in render/ and pulls in MTMathList from lib/. + @MainActor + func testModuleBuildsAndRendersCrossDirectoryTypes() { + let label = MTMathUILabel() + label.latex = #"\frac{1}{2}"# + XCTAssertNotNil(label.mathList) + XCTAssertNil(label.error) + + // Use the lib/ type directly as well so it stays in the consumed surface. + let list: MTMathList? = label.mathList + XCTAssertNotNil(list) + } +}