自定义Cordova插件:将框架添加到“嵌入二进制文件” [英] Custom Cordova Plugin: Add framework to "Embedded Binaries"

查看:847
本文介绍了自定义Cordova插件:将框架添加到“嵌入二进制文件”的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在自定义Cordova插件中,如何在plugin.xml中配置一个特定的.framework文件,以便将其添加到Xcode中的嵌入二进制文件部分?
如果目前不能直接在plugin.xml中使用,我可以选择其他建议。

解决方案

ve实现了一个解决方法,直到它由Cordova的 plugin.xml 支持,希望在将来,一个 embed 属性在这样的条目将具有相同效果:< framework embed =truesrc =.../> ,现在这个属性没有帮助,因此下面的解决方法。 p>

以下解决方案使用Cordova版本5.3.3。



首先,确保将框架条目添加到插件.xml:



 < framework src =pointToYour / File.frameworkembed =true/& 

embed =true



我们要创建一个钩子,在你的plugin.xml中声明:



 < hook type =after_platform_addsrc =hooks / embedframework / addEmbedded.js/& 

接下来,在我们的hook代码中有一个特定的节点模块, a href =https://github.com/alunny/node-xcode.git =nofollow noreferrer> node-xcode 。



安装node-xcode(必须是0.8.7版本或更高版本):



  npm i node-xcode 

最后,钩子的代码是 -



addEmbedded.js档案:



 '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; //插件只意味着为ios平台工作。
}
}

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 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); //必须从框架构建阶段删除,否则CodeSignOnCopy将不会做任何事情。
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,\$(继承)@ 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
var projectName = myProj.getFirstTarget()。firstTarget.name.substr(1);
projectName = projectName.substr(0,projectName.length-1); //删除字符开始和结束

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);

//为嵌入式框架添加PBXBuildFile
var file = {
uuid:fileId,
basename:justFrameworkFile,
settings:{
ATTRIBUTES:[CodeSignOnCopy,RemoveHeadersOnCopy]
},

fileRef:fileRef,
group:groupName
};
myProj.addToPbxBuildFileSection(file);


//添加到框架(单独的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);
};



这个钩子实际上是做什么的:




  1. 创建以您的插件标识命名的构建阶段,配置为复制文件,该副本的目标是框架。

  2. 查找并添加您的

  3. 设置名为 LD_RUNPATH_SEARCH_PATHS 的Xcode构建属性,以查找嵌入式框架在@ executable_path / Frameworks(这是嵌入式框架将被复制到复制文件 - >框架构建阶段

  4. 通过为.framework文件设置CodeSignOnCopy和RemoveHeadersOnCopy来配置ATTRIBUTES键。

  5. 从FrameworksBuildPhase中删除.framework文件,然后重新添加他们到FrameworksBuildPhase作为新的分离的PBXBuildFiles(相同的PBXFileReference),它必须完成为了CodeSignOnCopy的意思是任何东西,没有删除它,如果你打开项目与Xcode,你不会在生成中找到一个复选标记


  6. 更新1:hook代码,修改:



    $ b
  7. 添加了一个重要的修改,设置ATTRIBUTESCodeSignOnCopy和


改进了钩子以允许它在多个插件使用此钩子的情况下工作。

更新2




  1. 由于我 pull request 已被接受,不再需要安装自己的分支。


  2. $ b 根据Max Whaler的建议修改钩子脚本,因为我在Xcode 8上遇到过相同的问题。



    最后注释



    将您的应用程序上传到AppStore后,如果由于不支持的体系结构(i386等)验证失败,请尝试下面的Cordova插件(只有hook,没有本地代码): zcordova-plugin-archtrim


    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? If that's not currently possible directly in plugin.xml, I'm open to alternative suggestions.

    解决方案

    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.

    The following solution worked using Cordova version 5.3.3.

    First, make sure to add the framework entry to plugin.xml:

    <framework src="pointToYour/File.framework" embed="true" />
    

    embed="true" doesn't work for now, but add it anyway.

    We're gonna create a hook, declare that in your plugin.xml:

    <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.

    Install node-xcode (must be 0.8.7 version or above):

    npm i node-xcode
    

    Finally, the code for the hook is -

    addEmbedded.js file:

    '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);
    };
    

    What this hook actually does:

    1. Creates a "Build Phase" named after your plugin id, configured to "Copy Files", destination of that copy is "Frameworks".
    2. Finds and adds your .framework files to the above Build Phase, in turn, embedding it.
    3. Sets an Xcode build property named 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 Phase
    4. Configures the ATTRIBUTES key by setting "CodeSignOnCopy" and "RemoveHeadersOnCopy" for your .framework files.
    5. Removes your .framework files from the FrameworksBuildPhase and re-adds them to the FrameworksBuildPhase as new separated PBXBuildFiles (Same PBXFileReference), it has to be done in order for the "CodeSignOnCopy" to mean anything, without removing it, if you open the project with Xcode, you will not find a checkmark in the build phase that says it will sign it.

    Updated 1: hook code, modifications:

    1. The hook automatically finds your .framework files, no need to edit the hook.
    2. Added an important modification, setting ATTRIBUTES "CodeSignOnCopy" and "RemoveHeadersOnCopy" for your .framework files.
    3. Improved the hook to allow it to work in such a case where multiple plugins use this hook.

    Update 2

    1. Since my pull request has been accepted, there's no longer a need to install my own fork.
    2. Improved hook code.

    Update 3 (19/09/2016)

    Modified hook script according to Max Whaler's suggestion, as I experienced the same issue over Xcode 8.

    Final Note

    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

    这篇关于自定义Cordova插件:将框架添加到“嵌入二进制文件”的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆