CocoaPods / CocoaPods

The Cocoa Dependency Manager.
https://cocoapods.org/
Other
14.57k stars 2.63k forks source link

Build errors when linting projects that expose C++ in headers #5152

Open kastiglione opened 8 years ago

kastiglione commented 8 years ago

Using latest beta, there's an issue when linting a library that has C++ in its headers. The file CocoaPods/Lint/App/main.m is ObjC, and gets compilation errors when the library has C++ in its headers.

What did you do?

pod spec lint --private MyLibrary.podspec

What did you expected to happen?

I expected it to lint successfully. It does with the latest release of cocoapods (0.39.0) but not the latest pre-release (1.0.0.beta.6)

What happened instead?

Build errors pointing to CocoaPods/Lint/App/main.m. For example:

fatal error: 'unordered_map' file not found

or

error: unknown type name 'namespace'

Podfile

No Podfile is involved. This was linting a .podspec.

segiddins commented 8 years ago

Can you please share a podspec & its source files so we can reproduce the issue? Thanks!

kastiglione commented 8 years ago

Project: https://github.com/facebook/FBAllocationTracker Podspec: https://github.com/facebook/FBAllocationTracker/blob/master/FBAllocationTracker.podspec

thanks

neonichu commented 8 years ago

The issue here is that we generate an app target in 1.0 which imports a Pod to catch more errors during the linting process. The generated code is ObjC, though, which Pods like yours with C++ headers break, as you already discovered.

I see two possible fixes:

I think we can go with the first approach as headers probably won't hit any of the C vs. C++ incompatibilities and the second approach would likely be brittle.

kastiglione commented 8 years ago

First one sounds reasonable. Another possibility is generating as .m, compiling, and if that fails, re-generate as .mm, compile again, and if that works it's known to be C++. It would be slower linting for projects that use C++ in the headers, but would avoid any linkage issues for libraries that don't extern "C".

segiddins commented 8 years ago

@kastiglione running pod spec lint succeeds for me with FBAllocationTracker master

kastiglione commented 8 years ago

@segiddins The podspec has since been updated. It was version 0.1 that was failing.

segiddins commented 8 years ago

Hm so since those all still use .h extensions, I can't think of a good way of detecting this

neonichu commented 8 years ago

Unfortunately, there's also no real standard for C++ header extensions, it goes all the way from .h over .hh to even having no extension. :sob:

neonichu commented 8 years ago

Maybe we could switch to generating ObjC++ if library contains c++ — this doesn't necessarily mean that a Pod's header exposes C++, but at least we would limit this to Pods which are (partially) written in C++.

kastiglione commented 8 years ago

That's not a bad place to start.

segiddins commented 8 years ago

@neonichu but then that kinda screws of pods like Realm that use c++ but purposefully don't expose any to the user? idk, it just kinda sucks either way :P

jafara commented 8 years ago

Couldn't just the linter generate a dummy .mm file also? Xcode takes care of the rest.

segiddins commented 8 years ago

But that could (potentially) break imports of raw C headers

mdsb100 commented 8 years ago

@neonichu I have same trouble. But I can make the lint project be succeeded! Do this (Use --no-clean --use-libraries):

pod lib lint --sources='http://gitlab.baidao.com/ios/ytx-pod-specs.git,master' --verbose --use-libraries --no-clean

open the temporary lint project like

open /var/folders/w2/khz8t6h10q51lx_hvw8ds9wr0000gn/T/CocoaPods/Lint/App.xcworkspace

Rename "main.m" to "main.mm" in xcode. Modify "main.mm" like this: (Do not use @import )

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <YTXChart/YTXChart.h>
int main() {}

Run the command which is executed in 'pod lib lint'

xcodebuild clean build -workspace App.xcworkspace -scheme App -configuration Release CODE_SIGN_IDENTITY=- -sdk iphonesimulator -destination id=F76ED5E4-9AB6-4BC1-8BD1-87676A2B08CC

Finally, BUILD SUCCEEDED

Touch /Users/apple/Library/Developer/Xcode/DerivedData/App-beckcjxzcqwvwhbhxsbzztinnxlo/Build/Products/Release-iphonesimulator/App.app
    cd /var/folders/w2/khz8t6h10q51lx_hvw8ds9wr0000gn/T/CocoaPods/Lint
    export PATH="/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/usr/bin:/Applications/Xcode.app/Contents/Developer/usr/bin:/opt/local/bin:/opt/local/sbin:/Users/apple/bin:/usr/local/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin:/Users/apple/android-sdk/platform-tools:/Users/apple/android-sdk/tools:/Users/apple/apache-ant-1.9.2/bin:/Users/apple/oclint/bin:/Users/apple/.gem/ruby/2.0.0/bin"
    /usr/bin/touch -c /Users/apple/Library/Developer/Xcode/DerivedData/App-beckcjxzcqwvwhbhxsbzztinnxlo/Build/Products/Release-iphonesimulator/App.app

** BUILD SUCCEEDED **

So could you add a new parameter like '--use-c++' ? The new parameter will create a new temporary lint project. There is a "Main.mm" in this project.

This is my podspec:

Pod::Spec.new do |s|
  s.name             = "YTXChart"
  s.version          = "0.15.1"
  s.summary          = "YTXChart for pod"

# This description is used to generate tags and improve search results.
#   * Think: What does it do? Why did you write it? What is the focus?
#   * Try to keep it short, snappy and to the point.
#   * Write the description between the DESC delimiters below.
#   * Finally, don't worry about the indent, CocoaPods strips it!
  s.description      = "银天下Chart, 依赖AFNetworking"

  s.homepage         = "http://gitlab.baidao.com/ios/YTXChart.git"
  # s.screenshots     = "www.example.com/screenshots_1", "www.example.com/screenshots_2"
  s.license          = 'MIT'
  s.author           = { "caojun-mac" => "78612846@qq.com" }
  s.source           = { :git => "http://gitlab.baidao.com/ios/YTXChart.git", :tag => s.version }
  # s.social_media_url = 'https://twitter.com/<TWITTER_USERNAME>'

  s.platform     = :ios, '7.0'
  s.requires_arc = true

  s.source_files  = "Pod/Classes/painter/*.{h,m,mm}", "Pod/Classes/painterview/*.{h,m,mm}", "Pod/Classes/chart/*.{h,m,mm}", "Pod/Classes/core/*.{h,mm}", "Pod/Classes/core/**/*.{h,m,mm,inl}"

  # s.resource_bundles = {
  #   'YTXChart' => ['Pod/Assets/*.png']
  # }

  s.frameworks = 'Foundation', 'UIKit'
  s.libraries = 'sqlite3', 'c++'

  s.dependency 'YTXServerId'
  s.dependency 'AFNetworking', '~> 2.0'

end

PS: This repo lint succeeded at 0.39.0 and build succeeded in xcode GUI by using 'cmd+B'.

ezefranca commented 8 years ago

Hi @mdsb100

I have followed your steps and I come at the BUILD SUCCEEDED,

But I don't understood when I use the '--use-c++' parameters and finish my repo lint.

Can you explain a little more about this steps. Big Thanks.

mdsb100 commented 8 years ago

@ezefranca I just suggest the contributor to add '--use-c++' parameter for creating a lint project like my steps.

'.mm' would include static c++ library like 'std'. 'main.mm' is the entrance, so it must be a '.mm'.

We can add a static c++ library in XCode, but it does not work. Unless you rename the main.m to main.mm.

'@import' check this import-vs-import-ios-7

I do not know why the c++ project can not be a Module. May be this:

You don't actually need to use the @import keyword. If you opt-in to using modules, all #import and #include directives are mapped to use @import automatically. That means that you don't have to change your source code (or the source code of libraries that you download from elsewhere). Supposedly using modules improves the build performance too, especially if you haven't been using PCHs well or if your project has many small source files.

You can test simple c++ project and use '@import'.

mdsb100 commented 8 years ago

@segiddins When you plan to fix it?

segiddins commented 8 years ago

@mdsb100 we haven't really come up with a good fix yet, so that's a prerequisite to implementing one, sorry

mdsb100 commented 8 years ago

@segiddins Can you give a thought to my solution which is my previous comment in this issue. I have to switch to cocoapods@0.39.0 to lint and publish my c++/oc pod library.

segiddins commented 8 years ago

We don't want to add new command line options for linting unless strictly necessary

mikewoodworth commented 8 years ago

+1

Is there any workaround to this? At least a way to push to a private repo without passing the linting stage?

mdsb100 commented 8 years ago

@mikewoodworth Currently you have to switch 0.39.0 to lint and publish

angerman commented 8 years ago

I'd advocate for a --use-c++ flag, until a better solution is found. As it stands cocopods 1.0.1 is effectively useless as soon as c++ is used anywhere. Together with #5441 (support for symlinks; which is on hold), it seems like one should advocate to stick with 0.39.0 :(

a83988029 commented 8 years ago

@mdsb100 niubility!

mdsb100 commented 7 years ago

Is there new message?

We can not use cocoapod@0.39.0 for now. Because some third-party depended module must need cocoapod@1.0.0 to publish/lint!!!

We cant rewrite c++ to oc...

Please help me

a83988029 commented 7 years ago

@mdsb100 现在好像没这个问题了吧

mdsb100 commented 7 years ago

@a83988029 cocoapod@1.1.1 不能发啊 你能发?

a83988029 commented 7 years ago

@mdsb100 我能啊

a83988029 commented 7 years ago

@mdsb100 pod repo push private-repo 依赖c++header的xxx.podspec --allow-warnings --verbose --use-libraries

mdsb100 commented 7 years ago

你的库在不在github上 我去研究下

a83988029 commented 7 years ago

@mdsb100 不在github,我们用的公司内部的gitlab

mdsb100 commented 7 years ago

1.1.1么 我表示还是不行啊

a83988029 commented 7 years ago

@mdsb100 是1.1.1,我这没问题

mdsb100 commented 7 years ago

那你lint能过么?你们不lint的?

a83988029 commented 7 years ago

我不lint啊,push成功了不就行了么,push失败了也能看到错误在哪,一般不lint

mdsb100 commented 7 years ago

Finally, I figure out solution.

If you repo call the name is 'YTXChart', And you should add a 'YTXChart.h'. This header file does not contain any c++ header . In a word, 'YTXChart.h' can be empty. You can create a 'YTXChartHeader.h' contains origin content, this header can contain c++.

Lint passed! Test in cocoapods@1.1.1

Change your code in your main project:

<YTXChart/Chart.h>

to

<YTXChart/YTXChartHeader.h>

最终,我找到了一个解决方案。 如果你的repo名字是'YTXChart',你需要有一个叫做'YTXChart.h'的头文件。这个头文件不包含任何c++的内容(包括import的其他头文件)。一句话, 'YTXChart.h' 可以是空的。你可以增加一个'YTXChartHeader.h'包含原来头文件里的内容,这个头文件可以含有c++。

Lint 通过! 测试版本 cocoapods@1.1.1

在你的主工程里面把代码改成:

<YTXChart/Chart.h>

to

<YTXChart/YTXChartHeader.h>
a83988029 commented 7 years ago

@mdsb100 为啥一定要lint 直接push有什么问题吗

mdsb100 commented 7 years ago

当然要Lint,合规啊。我这边是lint不过push也不过的。

ntnmrndn commented 7 years ago

My workaround was to edit cocoapods source to use C++ then I could push.

(Don't worry this is a private repo)

mikewoodworth commented 7 years ago

@ntnmrndn any chance you can make this repo public/forkable? I think we're ready to go the same way. Perhaps we can all pool our efforts to maintain as a fork until they are ready to move on this?

ntnmrndn commented 7 years ago

@mikewoodworth I edited the file in place on my computer. I created a gist with the one file I changed (validator.rb) https://gist.github.com/ntnmrndn/5f91755c12812390b3ae8dec73c1154e

I don't think maintaining a fork is a good idea, since it would be incompatible with the main cocoa pods. We should however try to move forward on this issue.

ntnmrndn commented 7 years ago

To sum up the previous talks and current status, it seems

So far the only option I see is to add a field to the podspec.

This could looks like this:

# ――― Source Code ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
  #
  #  CocoaPods is smart about how it includes source code. For source files
  #  giving a folder will include any swift, h, m, mm, c & cpp files.
  #  For header files it will include any header in the folder.
  #  Not including the public_header_files will make all headers public.
  #

  s.source_files  = "Classes", "Classes/**/*.{h,m}"
  s.exclude_files = "Classes/Exclude"

  # s.public_header_files = "Classes/**/*.h"
  # s.public_cpp_header_files = "Classes/**/*.h[pp]?}"

If s.public_cpp_header_files contains files, we include them (and just them) in a separate main.mm file, while they are excluded from main.m compilation.

This should preserve backward compatibility and fix our issue.

If I could get feedback on this option quickly, I might find the time to implement it. Any thoughts ?

dnkoutso commented 7 years ago

@ntnmrndn I think that sounds fair. I think a few other maintainers should weigh in and then perhaps implement this.

beloso commented 7 years ago

That would be nice

dnkoutso commented 7 years ago

In 1.2.1.beta.1 a new option --skip-import-validation was added to match the behavior of 0.39.x. This means you can push Pods without performing linking (overall risky to do) but it should unblock most of the C++ pods that fail to link because the generated main.m does not work properly.

Until a much better solution is added I believe the flag should help most to publish.

See https://github.com/CocoaPods/CocoaPods/pull/6420

beloso commented 7 years ago

Is there no "setting" to say that headers in a specific folder are C++ headers?

Wouldn't that be a good solution? (I know nothing about implementing it)

EDIT: @ntnmrndn proposes that solution, it looks good to me 👍

GarryLance commented 7 years ago

使用了 --use-libraries 之后就可以了

priteshrnandgaonkar commented 6 years ago

Was able to solve the linting issue by changing app_target_helper.rb for cocoapod version 1.4.0 to this gist. It replicates the logic given by @ntnmrndn above

Sakari369 commented 1 year ago

I just spent many hours trying to fix this same thing. Why is this not mentioned anywhere on the CocoaPods website ?