Multi bundle support.

This commit is contained in:
Roman Telezhynskyi 2023-01-27 09:07:24 -08:00
parent 038afda6ed
commit e6c7051b6f
13 changed files with 243 additions and 44 deletions

View File

@ -10,4 +10,12 @@ VLib {
condition: i18nconfig.limitDeploymentOfQtTranslations condition: i18nconfig.limitDeploymentOfQtTranslations
windeployqt.languages: i18nconfig.qtTranslationLocales.join(',') windeployqt.languages: i18nconfig.qtTranslationLocales.join(',')
} }
installDebugInformation: qbs.buildVariant !== "release"
Properties {
condition: !qbs.targetOS.contains("macos") || (qbs.targetOS.contains("macos") && !buildconfig.enableMultiBundle)
install: true
installDir: buildconfig.installLibraryPath
}
} }

View File

@ -7,7 +7,7 @@ Library {
type: buildconfig.staticBuild ? "staticlibrary" : "dynamiclibrary" type: buildconfig.staticBuild ? "staticlibrary" : "dynamiclibrary"
buildconfig.appTarget: "valentina" buildconfig.appTarget: qbs.targetOS.contains("macos") ? "Valentina" : "valentina"
bundle.isBundle: buildconfig.frameworksBuild bundle.isBundle: buildconfig.frameworksBuild
cpp.includePaths: [".."] cpp.includePaths: [".."]
@ -24,9 +24,8 @@ Library {
cpp.compilerWrapper: "ccache" cpp.compilerWrapper: "ccache"
} }
install: !buildconfig.staticBuild install: false
installDir: buildconfig.installLibraryPath installDebugInformation: false
installDebugInformation: !buildconfig.staticBuild
Properties { Properties {
condition: qbs.targetOS.contains("macos") condition: qbs.targetOS.contains("macos")

View File

@ -17,6 +17,8 @@ VApp {
bundle.isBundle: qbs.buildVariant === "release" bundle.isBundle: qbs.buildVariant === "release"
bundle.identifierPrefix: 'ua.com.smart-pattern' bundle.identifierPrefix: 'ua.com.smart-pattern'
property bool primaryApp: false
bundle.infoPlist:({ bundle.infoPlist:({
"NSHumanReadableCopyright": buildconfig.valentina_copyright_string "NSHumanReadableCopyright": buildconfig.valentina_copyright_string
}) })
@ -74,7 +76,7 @@ VApp {
Rule { Rule {
multiplex: true multiplex: true
condition: qbs.targetOS.contains("macos") && bundle.isBundle condition: qbs.targetOS.contains("macos") && bundle.isBundle && (buildconfig.enableMultiBundle || primaryApp)
inputs: ["qm"] inputs: ["qm"]
outputFileTags: ["bundle.qm", "bundle.content"] outputFileTags: ["bundle.qm", "bundle.content"]
outputArtifacts: { outputArtifacts: {

View File

@ -22,6 +22,8 @@ Module {
property bool enableAppImage: false property bool enableAppImage: false
property bool enableMultiBundle: false
property string valentina_copyright_year: { return new Date().getFullYear().toString(); } property string valentina_copyright_year: { return new Date().getFullYear().toString(); }
property string valentina_copyright_string: "(C) 2013-" + valentina_copyright_year + ", Valentina project" property string valentina_copyright_string: "(C) 2013-" + valentina_copyright_year + ", Valentina project"
@ -117,6 +119,9 @@ Module {
if (enableAppImage && qbs.targetOS.contains("unix") && !qbs.targetOS.contains("macos")) if (enableAppImage && qbs.targetOS.contains("unix") && !qbs.targetOS.contains("macos"))
defines.push('APPIMAGE'); defines.push('APPIMAGE');
if (enableMultiBundle)
defines.push('MULTI_BUNDLE');
return defines; return defines;
} }

View File

@ -0,0 +1,130 @@
import qbs.File
Module {
additionalProductTypes: ["multibundle"]
property stringList targetApps: undefined
Rule {
// alwaysRun: true
multiplex: true
condition: product.qbs.targetOS.contains("macos") && product.buildconfig.enableMultiBundle && product.type.contains("dynamiclibrary")
inputs: product.bundle.isBundle ? ["bundle.content"] : ["dynamiclibrary"]
outputFileTags: ["multibundle"]
outputArtifacts: {
var artifactNames = [];
const fileName = product.bundle.isBundle ? product.bundle.bundleName : inputs["dynamiclibrary"][0].fileName;
const installRoot = product.qbs.installRoot + product.qbs.installPrefix + "/" + product.buildconfig.installAppPath;
product.multibundle.targetApps.forEach(function(targetApp) {
artifactNames.push(installRoot + "/" + targetApp + ".app/Contents/Frameworks/" + fileName);
if (product.installDebugInformation)
artifactNames.push(installRoot + "/" + targetApp + ".app/Contents/Frameworks/" + fileName +
product.cpp.debugInfoBundleSuffix);
});
var artifacts = artifactNames.map(function(art){
var a = {
filePath: art,
fileTags: ["multibundle"]
}
return a;
});
return artifacts;
}
prepare: {
var cmd = new JavaScriptCommand();
cmd.description = "Copying dynamic library into bundles";
cmd.highlight = "filegen";
const fileName = product.bundle.isBundle ? product.bundle.bundleName : inputs["dynamiclibrary"][0].fileName;
const installRoot = product.qbs.installRoot + product.qbs.installPrefix + "/" + product.buildconfig.installAppPath;
var data = [];
product.multibundle.targetApps.forEach(function(targetApp) {
data.push({
"source" : product.buildDirectory + "/" + fileName,
"destination": installRoot + "/" + targetApp + ".app/Contents/Frameworks/" + fileName
});
if (product.installDebugInformation)
data.push({
"source" : product.buildDirectory + "/" + fileName + product.cpp.debugInfoBundleSuffix,
"destination": installRoot + "/" + targetApp + ".app/Contents/Frameworks/" + fileName +
product.cpp.debugInfoBundleSuffix
});
});
cmd.data = data;
cmd.sourceCode = function() {
data.forEach(function(copyData) {
File.copy(copyData.source, copyData.destination);
});
};
return [cmd];
}
}
Rule {
// alwaysRun: true
condition: product.qbs.targetOS.contains("macos") && !product.buildconfig.enableMultiBundle && product.type.contains("application")
inputs: ["application"]
outputFileTags: ["multibundle"]
outputArtifacts: {
var artifactNames = [];
const installRoot = product.qbs.installRoot + product.qbs.installPrefix + "/" + product.buildconfig.installAppPath;
product.multibundle.targetApps.forEach(function(targetApp) {
artifactNames.push(installRoot + "/" + targetApp + ".app/Contents/MacOS/" + input.fileName);
if (product.installDebugInformation)
artifactNames.push(installRoot + "/" + targetApp + ".app/Contents/MacOS/" + input.fileName +
product.cpp.debugInfoBundleSuffix);
});
var artifacts = artifactNames.map(function(art){
var a = {
filePath: art,
fileTags: ["multibundle"]
}
return a;
});
return artifacts;
}
prepare: {
var cmd = new JavaScriptCommand();
cmd.description = "Copying auxiliary binary into bundle";
cmd.highlight = "filegen";
const fileName = product.bundle.isBundle ? product.bundle.bundleName : inputs["dynamiclibrary"][0].fileName;
const installRoot = product.qbs.installRoot + product.qbs.installPrefix + "/" + product.buildconfig.installAppPath;
var data = [];
product.multibundle.targetApps.forEach(function(targetApp) {
data.push({
"source" : input.filePath,
"destination": installRoot + "/" + targetApp + ".app/Contents/MacOS/" + input.fileName
});
if (product.installDebugInformation)
data.push({
"source" : product.buildDirectory + "/" + input.fileName + product.cpp.debugInfoBundleSuffix,
"destination": installRoot + "/" + targetApp + ".app/Contents/MacOS/" + fileName +
product.cpp.debugInfoBundleSuffix
});
});
cmd.data = data;
cmd.sourceCode = function() {
data.forEach(function(copyData) {
console.info("Dynamic source: " + copyData.source);
console.info("Dynamic destination: " + copyData.destination);
File.copy(copyData.source, copyData.destination);
});
};
return [cmd];
}
}
}

View File

@ -10,10 +10,12 @@ VToolApp {
Depends { name: "VFormatLib" } Depends { name: "VFormatLib" }
Depends { name: "VWidgetsLib" } Depends { name: "VWidgetsLib" }
Depends { name: "FervorLib" } Depends { name: "FervorLib" }
Depends { name: "multibundle"; }
name: "Puzzle" name: "Puzzle"
buildconfig.appTarget: qbs.targetOS.contains("macos") ? "Puzzle" : "puzzle" buildconfig.appTarget: qbs.targetOS.contains("macos") ? "Puzzle" : "puzzle"
targetName: buildconfig.appTarget targetName: buildconfig.appTarget
multibundle.targetApps: ["Valentina"]
files: [ files: [
"main.cpp", "main.cpp",
@ -176,7 +178,7 @@ VToolApp {
} }
Group { Group {
condition: qbs.targetOS.contains("macos") && qbs.architecture.contains("x86_64") condition: qbs.targetOS.contains("macos") && qbs.architecture.contains("x86_64") && buildconfig.enableMultiBundle
name: "pdftops MacOS" name: "pdftops MacOS"
prefix: project.sourceDirectory + "/dist/macx/bin64/" prefix: project.sourceDirectory + "/dist/macx/bin64/"
files: ["pdftops"] files: ["pdftops"]
@ -275,7 +277,7 @@ VToolApp {
Group { Group {
name: "MacOS assets" name: "MacOS assets"
condition: qbs.targetOS.contains("macos") condition: qbs.targetOS.contains("macos") && buildconfig.enableMultiBundle
prefix: project.sourceDirectory + "/dist/macx/puzzle/" prefix: project.sourceDirectory + "/dist/macx/puzzle/"
files: [ files: [
"Info.plist", "Info.plist",
@ -285,7 +287,7 @@ VToolApp {
Group { Group {
name: "ICNS" name: "ICNS"
condition: qbs.targetOS.contains("macos") condition: qbs.targetOS.contains("macos") && buildconfig.enableMultiBundle
prefix: project.sourceDirectory + "/dist/macx/valentina-project.xcassets/" prefix: project.sourceDirectory + "/dist/macx/valentina-project.xcassets/"
files: "layout.iconset" files: "layout.iconset"
} }

View File

@ -12,10 +12,12 @@ VToolApp {
Depends { name: "VWidgetsLib"; } Depends { name: "VWidgetsLib"; }
Depends { name: "VToolsLib"; } Depends { name: "VToolsLib"; }
Depends { name: "ebr" } Depends { name: "ebr" }
Depends { name: "multibundle"; }
name: "Tape" name: "Tape"
buildconfig.appTarget: qbs.targetOS.contains("macos") ? "Tape" : "tape" buildconfig.appTarget: qbs.targetOS.contains("macos") ? "Tape" : "tape"
targetName: buildconfig.appTarget targetName: buildconfig.appTarget
multibundle.targetApps: ["Valentina"]
files: [ files: [
"main.cpp", "main.cpp",
@ -220,7 +222,7 @@ VToolApp {
Group { Group {
name: "MacOS assets" name: "MacOS assets"
condition: qbs.targetOS.contains("macos") condition: qbs.targetOS.contains("macos") && buildconfig.enableMultiBundle
prefix: project.sourceDirectory + "/dist/macx/tape/" prefix: project.sourceDirectory + "/dist/macx/tape/"
files: [ files: [
"Info.plist", "Info.plist",
@ -230,7 +232,7 @@ VToolApp {
Group { Group {
name: "ICNS" name: "ICNS"
condition: qbs.targetOS.contains("macos") condition: qbs.targetOS.contains("macos") && buildconfig.enableMultiBundle
prefix: project.sourceDirectory + "/dist/macx/valentina-project.xcassets/" prefix: project.sourceDirectory + "/dist/macx/valentina-project.xcassets/"
files: [ files: [
"i-measurements.iconset", "i-measurements.iconset",

View File

@ -78,19 +78,42 @@ namespace
{ {
auto AppFilePath(const QString &appName) -> QString auto AppFilePath(const QString &appName) -> QString
{ {
QString appNameExe = appName; // NOLINT(performance-unnecessary-copy-initialization)
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
appNameExe += ".exe"; const QString executableSuffix = QStringLiteral(".exe");
#else
const QString executableSuffix;
#endif #endif
QFileInfo canonicalFile(QStringLiteral("%1/%2").arg(QCoreApplication::applicationDirPath(), appNameExe));
QFileInfo canonicalFile(QStringLiteral("%1/%2").arg(QCoreApplication::applicationDirPath(),
appName + executableSuffix));
if (canonicalFile.exists()) if (canonicalFile.exists())
{ {
return canonicalFile.absoluteFilePath(); return canonicalFile.absoluteFilePath();
} }
#if defined(Q_OS_MACOS) && defined(QBS_BUILD) && defined(MULTI_BUNDLE)
QFileInfo multiBundleFile(QStringLiteral("%1/../../../%2.app/Contents/MacOS/%2")
.arg(QCoreApplication::applicationDirPath(), appName));
if (multiBundleFile.exists())
{
return multiBundleFile.absoluteFilePath();
}
#endif
#if !defined(QBS_BUILD)
QFileInfo debugFile(QStringLiteral("%1/../../%2/bin/%3") QFileInfo debugFile(QStringLiteral("%1/../../%2/bin/%3")
.arg(QCoreApplication::applicationDirPath(), appName, appNameExe)); .arg(QCoreApplication::applicationDirPath(), appName, appName + executableSuffix));
return debugFile.exists() ? debugFile.absoluteFilePath() : appNameExe; if (debugFile.exists())
{
return debugFile.absoluteFilePath();
}
#endif
#if !defined(Q_OS_MACOS)
return appName + executableSuffix;
#else
return appName + QStringLiteral(".app");
#endif
} }
} // namespace } // namespace
@ -489,13 +512,23 @@ void VApplication::ActivateDarkMode()
//--------------------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------
auto VApplication::TapeFilePath() -> QString auto VApplication::TapeFilePath() -> QString
{ {
return AppFilePath(QStringLiteral("tape")); #ifdef Q_OS_MACOS
const QString appName = QStringLiteral("Tape");
#else
const QString appName = QStringLiteral("tape");
#endif
return AppFilePath(appName);
} }
//--------------------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------
auto VApplication::PuzzleFilePath() -> QString auto VApplication::PuzzleFilePath() -> QString
{ {
return AppFilePath(QStringLiteral("puzzle")); #ifdef Q_OS_MACOS
const QString appName = QStringLiteral("Puzzle");
#else
const QString appName = QStringLiteral("puzzle");
#endif
return AppFilePath(appName);
} }
//--------------------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------
@ -642,6 +675,32 @@ void VApplication::InitOptions()
ActivateDarkMode(); ActivateDarkMode();
} }
//---------------------------------------------------------------------------------------------------------------------
void VApplication::StartDetachedProcess(const QString &program, const QStringList &arguments)
{
#if !defined(Q_OS_MACOS)
const QString workingDirectory = QFileInfo(program).absoluteDir().absolutePath();
QProcess::startDetached(program, arguments, workingDirectory);
#else
if (not program.endsWith(".app"))
{
const QString workingDirectory = QFileInfo(program).absoluteDir().absolutePath();
QProcess::startDetached(program, arguments, workingDirectory);
}
else
{
QStringList openArguments {"-n", QStringLiteral("/Applications/%1").arg(program)};
if (not arguments.isEmpty())
{
openArguments.append("--args");
openArguments += arguments;
}
QProcess::startDetached("open", openArguments);
}
#endif
}
//--------------------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------
auto VApplication::LabelLanguages() -> QStringList auto VApplication::LabelLanguages() -> QStringList
{ {

View File

@ -51,6 +51,8 @@ public:
void InitOptions(); void InitOptions();
static void StartDetachedProcess(const QString &program, const QStringList &arguments);
static auto TapeFilePath() -> QString; static auto TapeFilePath() -> QString;
static auto PuzzleFilePath() -> QString; static auto PuzzleFilePath() -> QString;

View File

@ -2127,9 +2127,7 @@ void MainWindow::ShowMeasurements()
arguments.append(QStringLiteral("--") + LONG_OPTION_NO_HDPI_SCALING); arguments.append(QStringLiteral("--") + LONG_OPTION_NO_HDPI_SCALING);
} }
const QString tape = VApplication::TapeFilePath(); VApplication::StartDetachedProcess(VApplication::TapeFilePath(), arguments);
const QString workingDirectory = QFileInfo(tape).absoluteDir().absolutePath();
QProcess::startDetached(tape, arguments, workingDirectory);
} }
else else
{ {
@ -3803,16 +3801,13 @@ void MainWindow::on_actionOpen_triggered()
//--------------------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------
void MainWindow::on_actionOpenPuzzle_triggered() void MainWindow::on_actionOpenPuzzle_triggered()
{ {
const QString puzzle = VApplication::PuzzleFilePath();
const QString workingDirectory = QFileInfo(puzzle).absoluteDir().absolutePath();
QStringList arguments; QStringList arguments;
if (isNoScaling) if (isNoScaling)
{ {
arguments.append(QStringLiteral("--") + LONG_OPTION_NO_HDPI_SCALING); arguments.append(QStringLiteral("--") + LONG_OPTION_NO_HDPI_SCALING);
} }
QProcess::startDetached(puzzle, arguments, workingDirectory); VApplication::StartDetachedProcess(VApplication::PuzzleFilePath(), arguments);
} }
//--------------------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------
@ -3863,9 +3858,7 @@ void MainWindow::on_actionCreateManualLayout_triggered()
rldFile.setAutoRemove(false); rldFile.setAutoRemove(false);
const QString puzzle = VApplication::PuzzleFilePath(); VApplication::StartDetachedProcess(VApplication::PuzzleFilePath(), arguments);
const QString workingDirectory = QFileInfo(puzzle).absoluteDir().absolutePath();
QProcess::startDetached(puzzle, arguments, workingDirectory);
} }
else else
{ {
@ -3950,9 +3943,7 @@ void MainWindow::on_actionUpdateManualLayout_triggered()
rldFile.setAutoRemove(false); rldFile.setAutoRemove(false);
const QString puzzle = VApplication::PuzzleFilePath(); VApplication::StartDetachedProcess(VApplication::PuzzleFilePath(), arguments);
const QString workingDirectory = QFileInfo(puzzle).absoluteDir().absolutePath();
QProcess::startDetached(puzzle, arguments, workingDirectory);
} }
else else
{ {
@ -4742,16 +4733,13 @@ void MainWindow::ActionShowMainPath_triggered(bool checked)
//--------------------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------
void MainWindow::ActionOpenTape_triggered() void MainWindow::ActionOpenTape_triggered()
{ {
const QString tape = VApplication::TapeFilePath();
const QString workingDirectory = QFileInfo(tape).absoluteDir().absolutePath();
QStringList arguments; QStringList arguments;
if (isNoScaling) if (isNoScaling)
{ {
arguments.append(QStringLiteral("--") + LONG_OPTION_NO_HDPI_SCALING); arguments.append(QStringLiteral("--") + LONG_OPTION_NO_HDPI_SCALING);
} }
QProcess::startDetached(tape, arguments, workingDirectory); VApplication::StartDetachedProcess(VApplication::TapeFilePath(), arguments);
} }
//--------------------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------
@ -5833,16 +5821,13 @@ auto MainWindow::LoadPattern(QString fileName, const QString& customMeasureFile)
if (m.Type() == MeasurementsType::Multisize || m.Type() == MeasurementsType::Individual) if (m.Type() == MeasurementsType::Multisize || m.Type() == MeasurementsType::Individual)
{ {
const QString tape = VApplication::TapeFilePath(); QStringList arguments {fileName};
const QString workingDirectory = QFileInfo(tape).absoluteDir().absolutePath();
QStringList arguments = QStringList() << fileName;
if (isNoScaling) if (isNoScaling)
{ {
arguments.append(QStringLiteral("--") + LONG_OPTION_NO_HDPI_SCALING); arguments.append(QStringLiteral("--") + LONG_OPTION_NO_HDPI_SCALING);
} }
QProcess::startDetached(tape, arguments, workingDirectory); VApplication::StartDetachedProcess(VApplication::TapeFilePath(), arguments);
QCoreApplication::exit(V_EX_OK); QCoreApplication::exit(V_EX_OK);
return false; // stop continue processing return false; // stop continue processing
} }
@ -5864,16 +5849,13 @@ auto MainWindow::LoadPattern(QString fileName, const QString& customMeasureFile)
{ {
// Here comes undocumented Valentina's feature. // Here comes undocumented Valentina's feature.
// Because app bundle in Mac OS X doesn't allow setup assosiation for Puzzle we must do this through Valentina // Because app bundle in Mac OS X doesn't allow setup assosiation for Puzzle we must do this through Valentina
const QString puzzle = VApplication::PuzzleFilePath(); QStringList arguments {fileName};
const QString workingDirectory = QFileInfo(puzzle).absoluteDir().absolutePath();
QStringList arguments = QStringList() << fileName;
if (isNoScaling) if (isNoScaling)
{ {
arguments.append(QStringLiteral("--") + LONG_OPTION_NO_HDPI_SCALING); arguments.append(QStringLiteral("--") + LONG_OPTION_NO_HDPI_SCALING);
} }
QProcess::startDetached(puzzle, arguments, workingDirectory); VApplication::StartDetachedProcess(VApplication::PuzzleFilePath(), arguments);
QCoreApplication::exit(V_EX_OK); QCoreApplication::exit(V_EX_OK);
return false; // stop continue processing return false; // stop continue processing
} }

View File

@ -13,6 +13,8 @@ VToolApp {
Depends { name: "VFormatLib"; } Depends { name: "VFormatLib"; }
Depends { name: "VMiscLib"; } Depends { name: "VMiscLib"; }
primaryApp: true
Depends { Depends {
name: "Qt.winextras" name: "Qt.winextras"
condition: qbs.targetOS.contains("windows") condition: qbs.targetOS.contains("windows")

View File

@ -1,4 +1,6 @@
VDynamicLib { VDynamicLib {
Depends { name: "multibundle"; }
name: "QMUParserLib" name: "QMUParserLib"
version: "2.7.0" version: "2.7.0"
files: [ files: [
@ -49,4 +51,6 @@ VDynamicLib {
qbs.install: true qbs.install: true
qbs.installDir: buildconfig.installAppPath qbs.installDir: buildconfig.installAppPath
} }
multibundle.targetApps: ["Valentina", "Tape", "Puzzle"]
} }

View File

@ -1,6 +1,7 @@
VDynamicLib { VDynamicLib {
Depends { name: "Qt"; submodules: ["gui", "widgets"] } Depends { name: "Qt"; submodules: ["gui", "widgets"] }
Depends { name: "VMiscLib" } Depends { name: "VMiscLib" }
Depends { name: "multibundle"; }
name: "VPropertyExplorerLib" name: "VPropertyExplorerLib"
version: "1.0.0" version: "1.0.0"
@ -92,4 +93,5 @@ VDynamicLib {
} }
cpp.defines: "VPROPERTYEXPLORER_LIBRARY" cpp.defines: "VPROPERTYEXPLORER_LIBRARY"
multibundle.targetApps: ["Valentina", "Tape", "Puzzle"]
} }