[英]Custom Cordova Plugin: Add framework to “Embedded Binaries”
In a custom Cordova plugin, how can I config a specific .framework file in plugin.xml such that it will be added to the "Embedded Binaries" section in Xcode?在自定义 Cordova 插件中,如何在 plugin.xml 中配置特定的 .framework 文件,以便将其添加到 Xcode 的“嵌入式二进制文件”部分? If that's not currently possible directly in plugin.xml, I'm open to alternative suggestions.
如果目前无法直接在 plugin.xml 中实现,我愿意接受其他建议。
I've implemented a workaround until it's supported by Cordova's plugin.xml , hopefully, in the future, once an embed
property in such entries will have the same effect: <framework embed="true" src="..." />
, for now, this property does not help, hence the following workaround.我已经实施了一个变通方法,直到 Cordova 的plugin.xml支持它,希望将来,一旦此类条目中的
embed
属性将具有相同的效果: <framework embed="true" src="..." />
,目前,此属性没有帮助,因此有以下解决方法。
The following solution worked using Cordova version 5.3.3.以下解决方案使用 Cordova 5.3.3 版。
<framework src="pointToYour/File.framework" embed="true" />
embed="true"
doesn't work for now, but add it anyway. embed="true"
现在不起作用,但无论如何添加它。
<hook type="after_platform_add" src="hooks/embedframework/addEmbedded.js" />
Next, there's a specific node module we're gonna need in our hook's code, that module is node-xcode .接下来,我们的钩子代码中需要一个特定的节点模块,该模块是node-xcode 。
npm i xcode
Finally, the code for the hook itself -最后,钩子本身的代码 -
'use strict';
const xcode = require('xcode'),
fs = require('fs'),
path = require('path');
module.exports = function(context) {
if(process.length >=5 && process.argv[1].indexOf('cordova') == -1) {
if(process.argv[4] != 'ios') {
return; // plugin only meant to work for ios platform.
}
}
function fromDir(startPath,filter, rec, multiple){
if (!fs.existsSync(startPath)){
console.log("no dir ", startPath);
return;
}
const files=fs.readdirSync(startPath);
var resultFiles = []
for(var i=0;i<files.length;i++){
var filename=path.join(startPath,files[i]);
var stat = fs.lstatSync(filename);
if (stat.isDirectory() && rec){
fromDir(filename,filter); //recurse
}
if (filename.indexOf(filter)>=0) {
if (multiple) {
resultFiles.push(filename);
} else {
return filename;
}
}
}
if(multiple) {
return resultFiles;
}
}
function getFileIdAndRemoveFromFrameworks(myProj, fileBasename) {
var fileId = '';
const pbxFrameworksBuildPhaseObjFiles = myProj.pbxFrameworksBuildPhaseObj(myProj.getFirstTarget().uuid).files;
for(var i=0; i<pbxFrameworksBuildPhaseObjFiles.length;i++) {
var frameworkBuildPhaseFile = pbxFrameworksBuildPhaseObjFiles[i];
if(frameworkBuildPhaseFile.comment && frameworkBuildPhaseFile.comment.indexOf(fileBasename) != -1) {
fileId = frameworkBuildPhaseFile.value;
pbxFrameworksBuildPhaseObjFiles.splice(i,1); // MUST remove from frameworks build phase or else CodeSignOnCopy won't do anything.
break;
}
}
return fileId;
}
function getFileRefFromName(myProj, fName) {
const fileReferences = myProj.hash.project.objects['PBXFileReference'];
var fileRef = '';
for(var ref in fileReferences) {
if(ref.indexOf('_comment') == -1) {
var tmpFileRef = fileReferences[ref];
if(tmpFileRef.name && tmpFileRef.name.indexOf(fName) != -1) {
fileRef = ref;
break;
}
}
}
return fileRef;
}
const xcodeProjPath = fromDir('platforms/ios','.xcodeproj', false);
const projectPath = xcodeProjPath + '/project.pbxproj';
const myProj = xcode.project(projectPath);
function addRunpathSearchBuildProperty(proj, build) {
const LD_RUNPATH_SEARCH_PATHS = proj.getBuildProperty("LD_RUNPATH_SEARCH_PATHS", build);
if(!LD_RUNPATH_SEARCH_PATHS) {
proj.addBuildProperty("LD_RUNPATH_SEARCH_PATHS", "\"$(inherited) @executable_path/Frameworks\"", build);
} else if(LD_RUNPATH_SEARCH_PATHS.indexOf("@executable_path/Frameworks") == -1) {
var newValue = LD_RUNPATH_SEARCH_PATHS.substr(0,LD_RUNPATH_SEARCH_PATHS.length-1);
newValue += ' @executable_path/Frameworks\"';
proj.updateBuildProperty("LD_RUNPATH_SEARCH_PATHS", newValue, build);
}
}
myProj.parseSync();
addRunpathSearchBuildProperty(myProj, "Debug");
addRunpathSearchBuildProperty(myProj, "Release");
// unquote (remove trailing ")
var projectName = myProj.getFirstTarget().firstTarget.name.substr(1);
projectName = projectName.substr(0, projectName.length-1); //Removing the char " at beginning and the end.
const groupName = 'Embed Frameworks ' + context.opts.plugin.id;
const pluginPathInPlatformIosDir = projectName + '/Plugins/' + context.opts.plugin.id;
process.chdir('./platforms/ios');
const frameworkFilesToEmbed = fromDir(pluginPathInPlatformIosDir ,'.framework', false, true);
process.chdir('../../');
if(!frameworkFilesToEmbed.length) return;
myProj.addBuildPhase(frameworkFilesToEmbed, 'PBXCopyFilesBuildPhase', groupName, myProj.getFirstTarget().uuid, 'frameworks');
for(var frmFileFullPath of frameworkFilesToEmbed) {
var justFrameworkFile = path.basename(frmFileFullPath);
var fileRef = getFileRefFromName(myProj, justFrameworkFile);
var fileId = getFileIdAndRemoveFromFrameworks(myProj, justFrameworkFile);
// Adding PBXBuildFile for embedded frameworks
var file = {
uuid: fileId,
basename: justFrameworkFile,
settings: {
ATTRIBUTES: ["CodeSignOnCopy", "RemoveHeadersOnCopy"]
},
fileRef:fileRef,
group:groupName
};
myProj.addToPbxBuildFileSection(file);
// Adding to Frameworks as well (separate PBXBuildFile)
var newFrameworkFileEntry = {
uuid: myProj.generateUuid(),
basename: justFrameworkFile,
fileRef:fileRef,
group: "Frameworks"
};
myProj.addToPbxBuildFileSection(newFrameworkFileEntry);
myProj.addToPbxFrameworksBuildPhase(newFrameworkFileEntry);
}
fs.writeFileSync(projectPath, myProj.writeSync());
console.log('Embedded Frameworks In ' + context.opts.plugin.id);
};
LD_RUNPATH_SEARCH_PATHS
to also look for embedded frameworks in "@executable_path/Frameworks"
(That's were the embedded framework is going to be copied to after the "Copy Files"->"Frameworks" Build PhaseLD_RUNPATH_SEARCH_PATHS
的 Xcode 构建属性,以在"@executable_path/Frameworks"
查找嵌入的框架(即嵌入的框架将在“复制文件”->“框架”构建阶段之后被复制到 Modified hook script according to Max Whaler's suggestion, as I experienced the same issue over Xcode 8.根据 Max Whaler 的建议修改了钩子脚本,因为我在 Xcode 8 上遇到了同样的问题。
Once you upload your app to the AppStore, if validation fails due to unsupported architectures (i386, etc...), try the following Cordova plugin (only hook, no native code): zcordova-plugin-archtrim将应用程序上传到 AppStore 后,如果由于不支持的架构(i386 等...)导致验证失败,请尝试使用以下 Cordova 插件(仅挂钩,无本机代码): zcordova-plugin-archtrim
For adding libraries to "Embedded Binaries" section in Xcode (Starting from cordova-ios 4.4.0 and cordova 7.0.0), put this in your plugin.xml:要将库添加到 Xcode 中的“嵌入式二进制文件”部分(从cordova-ios 4.4.0 和cordova 7.0.0 开始),请将其放入您的plugin.xml 中:
<framework src="src/ios/XXX.framework" embed="true" custom="true" />
For adding libraries to "Linked Frameworks and Libraries" section in Xcode, put this in your plugin.xml:要将库添加到 Xcode 中的“链接框架和库”部分,请将其放入 plugin.xml:
<source-file src="src/ios/XXX.framework" target-dir="lib" framework="true" />
Both of them can exist at the same time.两者可以同时存在。 For example:
例如:
<!-- iOS Sample -->
<platform name="ios">
....
<source-file src="src/ios/XXX.m"/>
<source-file src="src/ios/XXX.framework" target-dir="lib" framework="true" />
<framework src="src/ios/XXX.framework" embed="true" custom="true" />
....
</platform>
<!-- Android Sample for your reference -->
<platform name="android">
....
<source-file src="src/android/XXX.java"/>
<framework src="src/android/build.gradle" custom="true" type="gradleReference" />
<resource-file src="src/android/SDK/libs/XXX.aar" target="libs/XXX.aar" />
....
</platform>
To get my plugin to build with a project on XCode 8.0 and cordova-ios 4.2, I had to run the hook in the after_build
phase.为了让我的插件在 XCode 8.0 和 cordova-ios 4.2 上构建项目,我必须在
after_build
阶段运行钩子。 Also, make sure that the node environment is using the latest verison of xcode-node (^0.8.9) or you will get bugs in the copy files phase.另外,请确保节点环境使用的是最新版本的 xcode-node (^0.8.9),否则您将在复制文件阶段遇到错误。
<framework src="lib/myCustom.framework" custom="true" embed="true" /> <hook type="after_build" src="hooks/add_embedded.js" />
The plugin.xml needs custom="true"
for Cordova to copy the framework file, which ended up conflicting with the changes made to the .pbxproj when this hook ran in after_platform add or even after_prepare. plugin.xml 需要
custom="true"
以便 Cordova 复制框架文件,当此钩子在 after_platform add 甚至 after_prepare 中运行时,最终与对 .pbxproj 所做的更改发生冲突。
embed="true"
is supported as of cordova-ios 4.4.0 and cordova 7.0.0, which was released today. embed="true"
今天发布的cordova-ios 4.4.0和cordova 7.0.0开始支持embed="true"
。 https://cordova.apache.org/docs/en/latest/plugin_ref/spec.html#framework https://issues.apache.org/jira/browse/CB-11233 https://cordova.apache.org/docs/en/latest/plugin_ref/spec.html#framework https://issues.apache.org/jira/browse/CB-11233
@Alon Amir, thanks for sharing, it works beautifully! @Alon Amir,感谢分享,效果很好! Although, my app ran perfectly in Debug but not in Release mode.
虽然,我的应用程序在 Debug 下完美运行,但在 Release 模式下运行不正常。 I figured out that the LD_RUNPATH_SEARCH_PATHS was only added to Debug mode as proj.getBuildProperty without a build parameter takes the first result.
我发现 LD_RUNPATH_SEARCH_PATHS 仅被添加到调试模式,因为没有构建参数的 proj.getBuildProperty 获取第一个结果。 I modified your code a bit so that it works in Debug as well as in Release mode:
我稍微修改了您的代码,以便它可以在 Debug 和 Release 模式下工作:
function addRunpathSearchBuildProperty(proj, build) {
const LD_RUNPATH_SEARCH_PATHS = proj.getBuildProperty("LD_RUNPATH_SEARCH_PATHS", build);
if(!LD_RUNPATH_SEARCH_PATHS) {
proj.addBuildProperty("LD_RUNPATH_SEARCH_PATHS", "\"$(inherited) @executable_path/Frameworks\"", build);
} else if(LD_RUNPATH_SEARCH_PATHS.indexOf("@executable_path/Frameworks") == -1) {
var newValue = LD_RUNPATH_SEARCH_PATHS.substr(0,LD_RUNPATH_SEARCH_PATHS.length-1);
newValue += ' @executable_path/Frameworks\"';
proj.updateBuildProperty("LD_RUNPATH_SEARCH_PATHS", newValue, build);
}
}
myProj.parseSync();
addRunpathSearchBuildProperty(myProj, "Debug");
addRunpathSearchBuildProperty(myProj, "Release");
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.