Topics - SquarePants1991/RNTopics GitHub Wiki

集成ReactNative源码到私有Pod Repo踩坑记

生成Podspec

Fork React Native代码。Clone代码到本地,切换到v0.52.0-rc.0 tag,然后建立自己的分支。这样方便做修改以及生成podspecs。

git clone https://github.com/SquarePants1991/react-native.git
git checkout v0.52.0-rc.0
git branch ht52rc0
git checkout ht52rc0

React Native源码中提供了一个脚本工具向本地的pod repo副本中写入RN的完整podspecs。我在我的github上建立了一个私有pod repo,我将它在本地命名为htspecs,可以使用pod repo命令查看本地的pod repo。

htspecs
- Type: git (master)
- URL:  [email protected]:SquarePants1991/HTPodSpecs.git
- Path: /Users/ocean/.cocoapods/repos/htspecs

使用RN源码下./scripts/process-podspecs.sh生成podspecs。从脚本代码可以看出,需要注册SPEC_REPO环境变量来控制podspecs被放到那个repo下。使用下面命令注册我的htspecsrepo。

export SPEC_REPO=htspecs

接着在RN源码根目录下运行

./scripts/process-podspecs.sh

你可能会因为下面的错误失败。

- ERROR | [iOS] xcodebuild:  /Users/ocean/Documents/Projects/RNPods/react-native-0.52/ReactCommon/yoga/yoga/YGNodePrint.cpp:208:46: error: implicit conversion loses integer precision: 'size_type' (aka 'unsigned long') to 'const uint32_t' (aka 'const unsigned int') [-Werror,-Wshorten-64-to-32]

这是因为yoga库开启了-Wall, -Werror编译选项,所有的警告被当作错误,然而在使用clang编译的时候会有一个-Wshorten-64-to-32警告,可能yoga的作者测试没有覆盖到,这个警告就被当作了错误,从而lint失败。这里我们可以先跳过lint。通过注册环境变量export SKIP_LINT=1就可以跳过lint了。执行完export SKIP_LINT=1后再运行./scripts/process-podspecs.sh基本就不会有问题了。

生成成功后,我们可以去~/.cocoapods/repos/htspecs/查看,应该会有下面这些文件夹。注意htspecs应该换成你的repo名字。

DoubleConversion/  
Folly/      
GLog/
React/
yoga/

上传podspec到私有repo

上传很简单,把~/.cocoapods/repos/htspecs/下的代码sync到master上就OK了。

为了验证,我们先更新下repo。

pod repo update

然后使用pod search React。就可以看见你的私有repo里的React了。可以从下面的Versions: 0.52.0-rc.0 [htspecs repo]看出这个Pod出自我的htspecs。

-> React (0.52.0-rc.0)
   A framework for building native apps using React
   pod 'React', '~> 0.52.0-rc.0'
   - Homepage: http://facebook.github.io/react-native/
   - Source:   https://github.com/facebook/react-native.git
   - Versions: 0.52.0-rc.0 [htspecs repo]
   - Subspecs:
     - React/Core (0.52.0-rc.0)

开始埋坑

Yoga podspec和编译错误

如果你以为前面两步走完就OK了,那就太顺利了。接着我们通过两种集成方式验证这个Pod的可用性,首先是静态库集成,就是不用framework。 新建demo项目,配置Podfile。这里我将HTPodSpecs作为首选source,这样pod install时会优先去HTPodSpecs里找React的Pod。

source '[email protected]:SquarePants1991/HTPodSpecs.git'
source 'https://github.com/CocoaPods/Specs.git'
#use_frameworks!

target 'RNPodTest_Example' do
    pod 'RNPodTest', :path => '../'
    pod 'React', '0.52.0-rc0', :subspecs => [
    'Core',
    'CxxBridge',
    'ART',
    'RCTAnimation',
    'RCTActionSheet',
    'RCTBlob',
    'RCTCameraRoll',
    'RCTGeolocation',
    'RCTImage',
    'RCTNetwork',
    'RCTPushNotification',
    'RCTSettings',
    'RCTText',
    'RCTVibration',
    'RCTWebSocket',
    'RCTLinkingIOS',
    'DevSupport',
    ]
    target 'RNPodTest_Tests' do
        inherit! :search_paths
        
        
    end
end

接着执行pod install。然后发现yoga安装失败了。

Analyzing dependencies
Fetching podspec for `RNPodTest` from `../`
Downloading dependencies
Installing DoubleConversion (1.1.5)
Installing Folly (2016.09.26.00)
Installing GLog (0.3.4)
Using RNPodTest (0.1.0)
Installing React (0.52.0-rc.0)
Installing boost (1.59.0)
Installing yoga (0.52.0-rc.0.React)

[!] Error installing yoga
[!] /usr/bin/git clone /Users/ocean/Documents/Projects/RNPods/react-native-0.52 /var/folders/zb/yn8531qj6kgc4y8ybr7cqdn00000gp/T/d20180109-38625-1wl6qwc --template= --single-branch --depth 1 --branch v0.52.0-rc.0

我们去看下yoga的podspec。惊奇的发现source指向的是本地路径,可能是我脚本使用的不是那么正确吧。不管这些,手动修正。

"source": {
    "git": "/Users/ocean/Documents/Projects/RNPods/react-native-0.52",
    "tag": "v0.52.0-rc.0"
  },

改成

"source": {
    "git": "https://github.com/facebook/react-native.git",
    "tag": "v0.52.0-rc.0"
  },

然后再次同步到master分支。接着pod updatedemo项目。因为repo内容改变了,所以需要pod update。或者你也可以单独更新某个repo pod repo update htspecs,更新成功后再pod install。这次一切OK。

Analyzing dependencies
Fetching podspec for `RNPodTest` from `../`
Downloading dependencies
Installing DoubleConversion (1.1.5)
Installing Folly (2016.09.26.00)
Installing GLog (0.3.4)
Using RNPodTest (0.1.0)
Installing React (0.52.0-rc.0)
Installing boost (1.59.0)
Installing yoga (0.52.0-rc.0.React)
Generating Pods project
Integrating client project
Sending stats
Pod installation complete! There are 18 dependencies from the Podfile and 7 total pods installed.

然后我们就可以开心的运行项目了。然而并不开心,新的错误又来了。。。

Users/ocean/Documents/Projects/Demos/RNPodTest/RNPodTest/Example/Pods/yoga/ReactCommon/yoga/yoga/YGNodePrint.cpp:208:46: error: implicit conversion loses integer precision: 'size_type' (aka 'unsigned long') to 'const uint32_t' (aka 'const unsigned int') [-Werror,-Wshorten-64-to-32]
  const uint32_t childCount = node->children.size();
                 ~~~~~~~~~~   ~~~~~~~~~~~~~~~^~~~~~
1 error generated.

这个其实就是我们在lint时忽略的错误。为什么会有这个错误呢?我们可以再看yoga的podspec,其中有这样一段配置。

"compiler_flags": [
    "-fno-omit-frame-pointer",
    "-fexceptions",
    "-Wall",
    "-Werror",
    "-std=c++1y",
    "-fPIC"
  ],

其中-Wall表示启用所有警告,-Werror表示把警告当作错误,这是个不错的习惯,但是百密一疏,clang下有一个-Wshorten-64-to-32的警告,大致就是精度损失的警告,结果就被当作错误,导致编译失败了。为了解决这个问题,目前我的方案就是把"-Werror"暂时去掉。去掉后再次把~/.cocoapods/repos/htspecs下的podspec同步到master上。再次pod update。 然后Build demo项目。

RCTTVRemoteHandler.h

接着新的错误出现了,'RCTTVRemoteHandler.h' file not found,于是我在项目中搜索了下关键字RCTTVRemoteHandler.h。 在RCTRootView中找到了有价值的参考代码。

#if TARGET_OS_TV
#import "RCTTVRemoteHandler.h"
#import "RCTTVNavigationEventEmitter.h"
#endif

出错的代码并没有使用#if TARGET_OS_TV来处理RCTTVRemoteHandler.h的导入。出错代码在RCTModalHostView.m中。怎么办呢?改源代码吧。将RCTModalHostView.m中的#import "RCTTVRemoteHandler.h"使用#if TARGET_OS_TV进行处理。

#if TARGET_OS_TV
#import "RCTTVRemoteHandler.h"
#endif

既然改了源代码,podspec里的source就得做相应改变。React和yoga的source都得改,将其中的git地址换成你修改过的源码地址。当然别忘了把你改过的代码commit,push到你的github上。

"source": {
    "git": "https://github.com/facebook/react-native.git",
    "tag": "v0.52.0-rc.0"
  },

改成

"source": {
    "git": "https://github.com/SquarePants1991/react-native.git",
    "branch": "ht52rc0"
  },

因为我后面会一直在ht52rc0分支上修改代码,为了方便,我将source直接指向了这个分支。再次将htspecs同步到master。然后pod update。Build项目,终于一切OK了。

支持use_framework

现在我们把Podfile里的use_frameworks!取消注释,来通过动态库集成ReactNative。再次pod update。然后Build项目。错误还是难免的。yoga又是第一个冒出来的。

While building module 'yoga' imported from /Users/ocean/Documents/Projects/Demos/RNPodTest/RNPodTest/Example/Pods/React/React/Views/UIView+React.h:13:
In file included from <module-includes>:1:
In file included from /Users/ocean/Documents/Projects/Demos/RNPodTest/RNPodTest/Example/Pods/Target Support Files/yoga/yoga-umbrella.h:15:
/Users/ocean/Documents/Projects/Demos/RNPodTest/RNPodTest/Example/Pods/yoga/ReactCommon/yoga/yoga/YGNodePrint.h:10:10: fatal error: 'string' file not found
#include <string>
         ^~~~~~~~
1 error generated.

为什么报错呢?由于UIView+React.h导入了<yoga/YGEnums.h>,因为是framework,系统导入了yoga整个module,然而yoga-umbrella.h里包含了yoga的所有头文件。包括不想暴露出去包括C++引用的头文件。UIView+React.h作为OC文件引入了包含#include <string>YGNodePrint.h,从而导致YGNodePrint.h也按照OC的规则编译,自然是找不到<string>。怎么解决呢?少暴露点头文件就好了,修改yoga的podspec文件。增加public_header_files。这样就不会把所有头文件都暴露出来了。

"public_header_files": [
        "ReactCommon/yoga/yoga/Yoga.h",
        "ReactCommon/yoga/yoga/YGMacros.h",
        "ReactCommon/yoga/yoga/YGEnums.h"
   ]

继续同步htspecs的repo,然后pod install,Build项目。没错,错误还在继续。又蹦出来个错误。在RCTNativeAnimatedNodesManager.h中。

...
#import <RCTAnimation/RCTValueAnimatedNode.h>   'RCTAnimation/RCTValueAnimatedNode.h' file not found

为什么不存在呢?我们去看下React的podspec,RCTAnimationReact的subspec,它在React中是这样配置的。其中有一个header_dir保证了Pods/Headers/Private下会有RCTAnimation子目录,从而可以使用#import <RCTAnimation/xxx.h>的方式导入RCTAnimation下的头文件。然而。。。use_framework!之后Pods/Headers直接就空了。所以header_dir相当与就没有用了。

{
      "name": "RCTAnimation",
      "dependencies": {
        "React/Core": [

        ]
      },
      "source_files": "Libraries/NativeAnimation/{Drivers/*,Nodes/*,*}.{h,m}",
      "header_dir": "RCTAnimation"
},

那怎么解决呢?最粗暴的方式就是把导入代码直接改了。

#import <RCTAnimation/RCTValueAnimatedNode.h>

改成

#import <React/RCTValueAnimatedNode.h>

可以直接在Demo中把Pod的代码改掉,看看效果。Build。。。接着出错。这次是#import <fishhook/fishhook.h>,原理和上面的一样。

#import <fishhook/fishhook.h>

改成

#import <React/fishhook.h>

继续Build,终于成功了。。。当然别忘了把这2个改动同步到你的rn源代码中。

追加

因为我一直在同一个分支修改代码,如果遇到修改代码,push后pod update代码还是老代码的情况。可以尝试清除项目里的Pods目录和~/Library/Caches/CocoaPods/Pods/Release/下相应的文件再pod install

这些是目前我所遇到的RN Cocoapods集成问题,如果后面还有遇到其他问题,会继续记录。

⚠️ **GitHub.com Fallback** ⚠️