Bazel: objc_library can not find header files

Created on 18 Sep 2020  路  8Comments  路  Source: bazelbuild/bazel

Description of the problem / feature request:

I'm trying to build a obj-c library but it seems the headers can not be found inside the library.

`Pods/Trustkit/Trustkit/TSKTrustKitConfig.m:12:9: fatal error: 'TSKTrustKitConfig.h' file not found

import "TSKTrustKitConfig.h"`

Bugs: what's the simplest, easiest way to reproduce this bug? Please provide a minimal example if possible.

objc_library(
    name = "Trustkit",
    srcs = glob(["Pods/Trustkit/Trustkit/**/*.m"], allow_empty=False),
    hdrs = glob(["Pods/Trustkit/Trustkit/**/*.h"], allow_empty=False),
)

What operating system are you running Bazel on?

MAC OS Catalina 10.5.6

What's the output of bazel info release?

Pods/Trustkit/Trustkit/TSKTrustKitConfig.m:12:9: fatal error: 'TSKTrustKitConfig.h' file not found
#import "TSKTrustKitConfig.h"

If bazel info release returns "development version" or "(@non-git)", tell us how you built Bazel.

bazel 3.5.0

support / not a bug (process) untriaged z-team-Apple

Most helpful comment

There are likely some other issues around about this, but in general this comes down to how bazel handles headers differently from Xcode. Bazel does not automatically map these header imports to the full path. You have a few remediation options depending on the case.

  1. You can do what google does and make headers absolute like #import "Pods/Trustkit/TrustKit/TSKTrustKitConfig.h". This likely won't work for cases where you don't own the library, and even if you did own the library you may not want to do this since it's a bit non-standard for iOS, and it can cause a lot of churn
  2. You can add an include flag to your copts to include this path, like copts = ["-IPods/Trustkit/Trustkit"]. This can work in the same module, but maybe not for consumers upstream
  3. You can add a directory to the includes opton like includes = ["Pods/Trustkit/Trustkit"] but this will propagate the include flags to all reverse transitive dependencies, which you may not want since it could over-include things, and might balloon your compiler invocations (which could lead to other issues)
  4. You can use headermaps with a custom rule set (bazel does not support this directly) like the one from here https://github.com/bazel-ios/rules_ios/blob/master/docs/hmap_doc.md (this is closest to what Xcode does in most cases)

There might be some more options here, but this covers the general idea

All 8 comments

There are likely some other issues around about this, but in general this comes down to how bazel handles headers differently from Xcode. Bazel does not automatically map these header imports to the full path. You have a few remediation options depending on the case.

  1. You can do what google does and make headers absolute like #import "Pods/Trustkit/TrustKit/TSKTrustKitConfig.h". This likely won't work for cases where you don't own the library, and even if you did own the library you may not want to do this since it's a bit non-standard for iOS, and it can cause a lot of churn
  2. You can add an include flag to your copts to include this path, like copts = ["-IPods/Trustkit/Trustkit"]. This can work in the same module, but maybe not for consumers upstream
  3. You can add a directory to the includes opton like includes = ["Pods/Trustkit/Trustkit"] but this will propagate the include flags to all reverse transitive dependencies, which you may not want since it could over-include things, and might balloon your compiler invocations (which could lead to other issues)
  4. You can use headermaps with a custom rule set (bazel does not support this directly) like the one from here https://github.com/bazel-ios/rules_ios/blob/master/docs/hmap_doc.md (this is closest to what Xcode does in most cases)

There might be some more options here, but this covers the general idea

Thank you very much again @keith , it seems you are on the top of everything here 馃憤

FYI here's an example macro that would make option 2 above easier https://github.com/bazelbuild/bazel/issues/2670#issuecomment-369674735

+1 to what @keith mentioned to use a headermap rule and adding many includes will really bog down clang/swift due to file system lookups on each directory.

Also another strategy if you've got a handful of dependencies - PodToBUILD can auto-generate BUILD files with the new_pod_repository workspace rule or copy pod source code into your tree. Overall, I've found it to be helpful to manage/update a _ton_ of BUILD files using the .podspec as the source of truth. It works with most cocoapods out of the box and recently got support for swift + mixed modules. It uses headermaps if you set generate_header_map =True with the llvm based headermap rule.

Both Number 4 and PodsToBuild worked for the header issue.

Now I have a mixed cocoa framework(Obj-C and Swift) to build. Any suggestions about to handle that @jerrymarino and @keith ?

The Apple rules don't support mixed source frameworks (and likely never will at this point). I would recommend you break them up if possible. If you cannot there is a rule for this in this repo https://github.com/bazel-ios/rules_ios

Is this ready to be closed, or is there a need for a fix or documentation?

I think it would be nice if this documentation lived somewhere, but I'm not sure there's a good place for this, I think this can be closed.

Was this page helpful?
0 / 5 - 0 ratings