В настраиваемом плагине Cordova, как я могу настроить конкретный файл .framework в файле plugin.xml, чтобы он был добавлен в раздел "Встроенные двоичные файлы" в Xcode? Если это невозможно в настоящее время непосредственно в plugin.xml, я открыт для альтернативных предложений.
Пользовательский плагин Cordova: добавьте фреймворк к "встроенным двоичным файлам"
Ответ 1
Я применил обходное решение, пока оно не будет поддерживаться Cordova plugin.xml, надеюсь, в будущем, как только свойство embed
в таких записях будет иметь такой же эффект: <framework embed="true" src="..." />
, на данный момент это свойство не помогите, следовательно, следующее обходное решение.
Следующее решение работало с использованием Кордовы версии 5.3.3.
Во-первых, обязательно добавьте запись фрейма в plugin.xml:
<framework src="pointToYour/File.framework" embed="true" />
embed="true"
не работает, но добавьте его в любом случае.
Мы создадим крючок, объявим, что в вашем plugin.xml:
<hook type="after_platform_add" src="hooks/embedframework/addEmbedded.js" />
Далее, существует специальный модуль node, который нам понадобится в нашем коде hook, этот модуль node-xcode.
Установить node -xcode (должна быть версия 0.8.7 или выше):
npm i 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; // 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);
};
Что действительно делает этот крючок:
- Создает "фазу сборки", названную после вашего идентификатора плагина, настроенного на "Копировать файлы", назначение этой копии - "Рамки".
- Находит и добавляет ваши файлы .framework к вышеуказанной фазе сборки, в свою очередь, встраивая ее.
- Устанавливает свойство сборки Xcode с именем
LD_RUNPATH_SEARCH_PATHS
, чтобы также искать встроенные фреймворки в"@executable_path/Frameworks"
(Это была встроенная инфраструктура, которая будет скопирована после "Скопировать файлы" → "Рамки" Build Phase - Настраивает ключ ATTRIBUTES, устанавливая "CodeSignOnCopy" и "RemoveHeadersOnCopy" для ваших файлов .framework.
- Удаляет ваши файлы .framework из FrameworksBuildPhase и повторно добавляет их в FrameworksBuildPhase в качестве новых разделенных PBXBuildFiles (Same PBXFileReference), это должно быть сделано для того, чтобы "CodeSignOnCopy" означал что угодно, не удаляя его, если вы откройте проект с помощью Xcode, вы не найдете галочку в фазе сборки, которая говорит, что она подпишет ее.
Обновлено 1: код крюка, изменения:
- Крючок автоматически находит ваши файлы .framework, нет необходимости редактировать hook.
- Добавлена важная модификация, устанавливающая ATTRIBUTES "CodeSignOnCopy" и "RemoveHeadersOnCopy" для ваших файлов .framework.
- Улучшен захват, чтобы он работал в таком случае, когда несколько подключаемых модулей используют этот hook.
Обновление 2
- Поскольку мой запрос был принят, вам больше не нужно устанавливать мою собственную вилку.
- Улучшен код hook.
Обновление 3 (19/09/2016)
Измененный крюк script в соответствии с предложением Max Whaler, поскольку я столкнулся с той же проблемой по сравнению с Xcode 8.
Заключительная записка
После того, как вы загрузите приложение в AppStore, если проверка завершится неудачно из-за неподдерживаемых архитектур (i386 и т.д.), попробуйте следующий плагин Cordova (только для hook, нет собственного кода): zcordova-plugin-archtrim
Ответ 2
Чтобы получить мой плагин для сборки с проектом на XCode 8.0 и cordova-ios 4.2, мне пришлось запустить hook в фазе after_build
. Кроме того, убедитесь, что среда node использует последнюю версию xcode- node (^ 0.8.9) или вы получите ошибки на фазе файлов копий.
<framework src="lib/myCustom.framework" custom="true" embed="true" />
<hook type="after_build" src="hooks/add_embedded.js" />
Плагин .xml нуждается в custom="true"
для Кордовы, чтобы скопировать файл фреймворка, что в итоге противоречило изменениям, внесенным в .pbxproj, когда этот крюк запускался в after_platform add или даже after_prepare.
Ответ 3
embed="true"
поддерживается как от cordova-ios 4.4.0, так и от cordova 7.0.0, который был выпущен сегодня.
https://cordova.apache.org/docs/en/latest/plugin_ref/spec.html#framework
https://issues.apache.org/jira/browse/CB-11233
Ответ 4
Для добавления библиотек в раздел "Встроенные двоичные файлы" в Xcode (Начиная с cordova-ios 4.4.0 и cordova 7.0.0) поместите это в свой файл plugin.xml:
<framework src="src/ios/XXX.framework" embed="true" custom="true" />
Чтобы добавить библиотеки в раздел "Связанные структуры и библиотеки" в Xcode, поместите это в свой файл plugin.xml:
<source-file src="src/ios/XXX.framework" target-dir="lib" framework="true" />
Оба они могут существовать одновременно. Например:
<!-- 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>
Ответ 5
@Alon Amir, спасибо за обмен, он работает красиво! Хотя мое приложение отлично работает в Debug, но не в режиме Release. Я понял, что LD_RUNPATH_SEARCH_PATHS был добавлен только в режим Debug, поскольку proj.getBuildProperty без параметра build принимает первый результат. Я немного изменил ваш код, чтобы он работал в 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");