首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >自定义Cordova插件:向“嵌入式二进制文件”添加框架

自定义Cordova插件:向“嵌入式二进制文件”添加框架
EN

Stack Overflow用户
提问于 2016-04-15 14:56:42
回答 5查看 15.4K关注 0票数 16

在自定义的Cordova插件中,我如何在plugin.xml中配置特定的plugin.xml文件,以便将其添加到Xcode中的“嵌入式二进制文件”部分?如果这目前在plugin.xml中是不可能的,我愿意接受其他建议。

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2016-04-19 15:59:08

我已经实现了一个解决方案,直到它得到Cordova的plugin.xml的支持,希望在将来,一旦这些条目中的embed属性具有相同的效果:<framework embed="true" src="..." />,就目前而言,这个属性没有帮助,因此下面的解决办法如下。

下面的解决方案使用了Cordova版本5.3.3。

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

代码语言:javascript
复制
<framework src="pointToYour/File.framework" embed="true" />

embed="true"暂时不起作用,但无论如何都要加进去。

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

代码语言:javascript
复制
<hook type="after_platform_add" src="hooks/embedframework/addEmbedded.js" />

接下来,在钩子的代码中需要一个特定的节点模块,这个模块是节点-xcode

安装节点-xcode(必须为0.8.7版本或更高版本):

代码语言:javascript
复制
npm i xcode

最后,钩子本身的代码-

addEmbedded.js文件:

代码语言:javascript
复制
'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);
};

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

  1. 创建一个以插件id命名的“构建阶段”,配置为“复制文件”,该副本的目标是“框架”。
  2. 查找并将您的.framework文件添加到上述构建阶段,然后嵌入它。
  3. 设置名为LD_RUNPATH_SEARCH_PATHS的Xcode build属性,以查找"@executable_path/Frameworks"中的嵌入式框架(也就是说,嵌入式框架将被复制到“复制文件”->框架“构建阶段”之后
  4. 通过为您的CodeSignOnCopy文件设置"RemoveHeadersOnCopy“和”.framework“来配置属性键。
  5. 从.framework中移除您的FrameworksBuildPhase文件,并将它们重新添加到FrameworksBuildPhase中,作为新的分离的PBXBuildFiles (相同的PBXFileReference),为了使"CodeSignOnCopy“有任何意义,必须这样做,而不删除它,如果您用Xcode打开项目,您将不会在构建阶段找到一个表示它将对其签名的复选标记。

更新1:钩子代码,修改:

  1. 钩子自动找到您的.framework文件,不需要编辑钩子。
  2. 添加了一个重要的修改,为您的CodeSignOnCopy文件设置属性"RemoveHeadersOnCopy“和”RemoveHeadersOnCopy“。
  3. 改进了钩子,允许它在多个插件使用这个钩子的情况下工作。

更新2

  1. 自从我的拉请求被接受以来,就不再需要安装我自己的分叉了。
  2. 改进的钩子代码。

更新3(2016年9月19日)

根据Max的建议修改钩子脚本,因为我在Xcode 8上经历了同样的问题。

最后注

一旦您将应用程序上传到AppStore,如果由于不支持的体系结构(i386等)而导致验证失败,请尝试以下Cordova插件(只有钩子,没有本机代码):zcordova插件

票数 34
EN

Stack Overflow用户

发布于 2018-04-24 10:26:42

要将库添加到Xcode中的“嵌入式二进制文件”部分(从cordova-ios 4.4.0和cordova 7.0.0开始),将其放在您的plugin.xml中:

代码语言:javascript
复制
<framework src="src/ios/XXX.framework"   embed="true" custom="true" />

要将库添加到Xcode中的“链接框架和库”部分,请将其放在您的plugin.xml中:

代码语言:javascript
复制
<source-file src="src/ios/XXX.framework" target-dir="lib" framework="true" />

两者可以同时存在。例如:

代码语言:javascript
复制
<!-- 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>
票数 11
EN

Stack Overflow用户

发布于 2016-10-26 06:52:06

为了让我的插件在XCode 8.0和cordova-ios 4.2上构建一个项目,我不得不在after_build阶段运行钩子。另外,确保节点环境使用的是xcode-节点的最新版本(^0.8.9),否则会在复制文件阶段出现bug。

<framework src="lib/myCustom.framework" custom="true" embed="true" /> <hook type="after_build" src="hooks/add_embedded.js" />

plugin.xml需要custom="true"来复制框架文件,这与在after_platform add甚至after_prepare中运行这个钩子时对.pbxproj所做的更改相冲突。

票数 7
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/36650522

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档