diff --git a/cli.js b/cli.js index ff2c2e0..15ebeda 100644 --- a/cli.js +++ b/cli.js @@ -1,93 +1,91 @@ -#!/usr/bin/env node - -const fs = require("fs-extra"); -const path = require("path"); -console.log("Revery Packager v" + require("./package.json").version); - -const args = require("yargs") - .argv; - -let projectDir = process.cwd(); - -if (args.project) { - projectDir = args.project; -} - -console.log(" - Packaging project: " + projectDir); - -const releaseDir = path.join(projectDir, "_release"); -const platformReleaseDir = path.join(releaseDir, process.platform); - -console.log(" - Placing release artifacts in: " + platformReleaseDir); - -const packageJsonPath = path.join(projectDir, "package.json"); - -if (!fs.existsSync(packageJsonPath)) { - throw "No package.json found at: " + packageJsonPath; -}; - -const packageInfo = require(packageJsonPath); - -console.log(" - Checking esy status..."); - -const esy = require("./src/esy"); - -const isBuilt = esy.isBuilt(projectDir); - -if (!isBuilt) { - throw "Esy project must be built prior to packaging."; -} - -console.log (" - Esy isBuilt: true"); - -const workingDirectory = projectDir; -const binPath = esy.getEsyVariable(workingDirectory, "self.bin"); -const reveryBinPath = esy.getEsyVariable(workingDirectory, "revery.bin"); - -console.log(" - Project bin path: " + binPath); -console.log(" - Revery bin path: " + reveryBinPath); - - -let macBundlerPath = null; -if (process.platform == "darwin") { - let macBundlerDir = esy.getEsyVariable(workingDirectory, "esy-macdylibbundler.bin"); - macBundlerPath = path.join(macBundlerDir, "dylibbundler"); - - // TODO: Can we do something better, like bundle esy-dylibbundler with this project? - if (!fs.existsSync(macBundlerPath)) { - throw "esy-macdylibbundler must be a dependency of the project."; - } - console.log(" - Mac bundler path: " + macBundlerPath); -} - -console.log("Created _release directory"); -fs.removeSync(platformReleaseDir); -fs.mkdirpSync(platformReleaseDir); - -esy.ensureInstalled(workingDirectory); - -const bundleInfo = require("./src/bundle").getBundleInfo(packageInfo); - -const config = { - projectDir, - releaseDir, - platformReleaseDir, - bundleInfo, - packageInfo, - binPath, - reveryBinPath, - macBundlerPath, -}; - -const run = async () => { - - if (process.platform == "win32") { - await require("./src/package-windows")(config); - } else if (process.platform == "darwin") { - await require("./src/package-darwin")(config); - } else { - await require("./src/package-linux")(config); - } -}; - -run(); +#!/usr/bin/env node + +const fs = require("fs-extra"); +const path = require("path"); +console.log("Revery Packager v" + require("./package.json").version); + +const args = require("yargs") + .argv; + +let projectDir = process.cwd(); + +if (args.project) { + projectDir = args.project; +} + +console.log(" - Packaging project: " + projectDir); + +const releaseDir = path.join(projectDir, "_release"); +const platformReleaseDir = path.join(releaseDir, process.platform); + +console.log(" - Placing release artifacts in: " + platformReleaseDir); + +const packageJsonPath = path.join(projectDir, "package.json"); + +if (!fs.existsSync(packageJsonPath)) { + throw "No package.json found at: " + packageJsonPath; +}; + +const packageInfo = require(packageJsonPath); + +console.log(" - Checking esy status..."); + +const esy = require("./src/esy"); + +const isBuilt = esy.isBuilt(projectDir); + +if (!isBuilt) { + throw "Esy project must be built prior to packaging."; +} + +console.log (" - Esy isBuilt: true"); + +const workingDirectory = projectDir; +const binPath = esy.getEsyVariable(workingDirectory, "self.bin"); +const reveryBinPath = esy.getEsyVariable(workingDirectory, "revery.bin"); + +console.log(" - Project bin path: " + binPath); +console.log(" - Revery bin path: " + reveryBinPath); + +let macBundlerPath = null; +if (process.platform == "darwin") { + let macBundlerDir = esy.getEsyVariable(workingDirectory, "esy-macdylibbundler.bin"); + macBundlerPath = path.join(macBundlerDir, "dylibbundler"); + + // TODO: Can we do something better, like bundle esy-dylibbundler with this project? + if (!fs.existsSync(macBundlerPath)) { + throw "esy-macdylibbundler must be a dependency of the project."; + } + console.log(" - Mac bundler path: " + macBundlerPath); +} + +console.log("Created _release directory"); +fs.removeSync(platformReleaseDir); +fs.mkdirpSync(platformReleaseDir); + +esy.ensureInstalled(workingDirectory); + +const bundleInfo = require("./src/bundle").getBundleInfo(packageInfo); + +const config = { + projectDir, + releaseDir, + platformReleaseDir, + bundleInfo, + packageInfo, + binPath, + reveryBinPath, + macBundlerPath, +}; + +const run = async () => { + if (process.platform == "win32") { + await require("./src/package-windows")(config); + } else if (process.platform == "darwin") { + await require("./src/package-darwin")(config); + } else { + await require("./src/package-linux")(config); + } +}; + +run(); diff --git a/src/bundle.js b/src/bundle.js index b56af32..04ee34e 100644 --- a/src/bundle.js +++ b/src/bundle.js @@ -1,65 +1,65 @@ -// Bundle.js -// -// Utilities for reading the bundle information from package json - -const path = require("path"); -const fs = require("fs-extra"); - -const defaultBundleInfo = { - // On Mac, this is the name of the app bundle. For example, - // if bundleName is "ExampleApp", the output App would be "Example.App" - bundleName: "ReveryApp", - - // On Mac, this is the bundle id used in the plist - bundleId: "com.example.revery", - - displayName: "Revery App", - - // Main executable should be the primary executable, WITHOUT the '.exe' extension. - mainExecutable: "App", - - // Packages - list of package formats to output: - packages: ["zip", "tar", "dmg", "appimage"], - - // MAC ONLY: The background to use for the dmg - dmgBackground: path.join(__dirname, "..", "assets", "dmg-background.png"), - - - appImageType: "Application", - appImageCategory: "Development", - - // Path to icon file - // Windows: An .ico file is expected - // OSX: An .icns file is expected - iconFile: path.join(__dirname, "..", "assets", "revery-icon.png"), -}; - -const getBundleInfo = (packageJson) => { - - let platform; - if (process.platform == "win32") { - platform = "win32"; - } else if (process.platform == "darwin") { - platform = "darwin" - } else { - platform = "linux" - } - - let commonBundleInfo = packageJson["revery-packager"] || {}; - - let platformBundleInfo = {}; - - if (commonBundleInfo[platform]) { - platformBundleInfo = commonBundleInfo[platform]; - } - - return { - ...defaultBundleInfo, - ...commonBundleInfo, - ...platformBundleInfo - }; -}; - -module.exports = { - getBundleInfo -}; +// Bundle.js +// +// Utilities for reading the bundle information from package json + +const path = require("path"); +const fs = require("fs-extra"); + +const defaultBundleInfo = { + // On Mac, this is the name of the app bundle. For example, + // if bundleName is "ExampleApp", the output App would be "Example.App" + bundleName: "ReveryApp", + + // On Mac, this is the bundle id used in the plist + bundleId: "com.example.revery", + + displayName: "Revery App", + + // Main executable should be the primary executable, WITHOUT the '.exe' extension. + mainExecutable: "App", + + // Packages - list of package formats to output: + packages: ["zip", "tar", "dmg", "appimage"], + + // MAC ONLY: The background to use for the dmg + dmgBackground: path.join(__dirname, "..", "assets", "dmg-background.png"), + + + appImageType: "Application", + appImageCategory: "Development", + + // Path to icon file + // Windows: An .ico file is expected + // OSX: An .icns file is expected + iconFile: path.join(__dirname, "..", "assets", "revery-icon.png"), +}; + +const getBundleInfo = (packageJson) => { + + let platform; + if (process.platform == "win32") { + platform = "win32"; + } else if (process.platform == "darwin") { + platform = "darwin" + } else { + platform = "linux" + } + + let commonBundleInfo = packageJson["revery-packager"] || {}; + + let platformBundleInfo = {}; + + if (commonBundleInfo[platform]) { + platformBundleInfo = commonBundleInfo[platform]; + } + + return { + ...defaultBundleInfo, + ...commonBundleInfo, + ...platformBundleInfo + }; +}; + +module.exports = { + getBundleInfo +}; diff --git a/src/esy.js b/src/esy.js index 2e3eb04..fa6c775 100644 --- a/src/esy.js +++ b/src/esy.js @@ -1,41 +1,40 @@ - -const cp = require("child_process"); - -const esyCommand = process.platform == "win32" ? "esy.cmd" : "esy"; - -const runEsyCommand = (workingDirectory, args) => { - console.log("ESY: Running esy command: " + args.join(" ") + " in " + workingDirectory); - - const result = cp.spawnSync(esyCommand, args, { cwd: workingDirectory, env: process.env}); - if (!result) { - return null; - } else if (!result.stdout) { - return null; - } else { - const out = result.stdout.toString("utf-8").trim(); - return out; - } -}; - -const getEsyVariable = (workingDirectory, variableName) => { - return runEsyCommand(workingDirectory, ["echo", "#{" + variableName +"}"]); -}; - -const isBuilt = (workingDirectory) => { - const json = runEsyCommand(workingDirectory, ["status"]); - if (!json) { - return false; - } - - return JSON.parse(json).isProjectReadyForDev; -}; - -const ensureInstalled = (workingDirectory) => { - runEsyCommand(workingDirectory, ["x", "echo"]); -}; - -module.exports = { - ensureInstalled, - getEsyVariable, - isBuilt, -}; +const cp = require("child_process"); + +const esyCommand = process.platform == "win32" ? "esy.cmd" : "esy"; + +const runEsyCommand = (workingDirectory, args) => { + console.log("ESY: Running esy command: " + args.join(" ") + " in " + workingDirectory); + + const result = cp.spawnSync(esyCommand, args, { cwd: workingDirectory, env: process.env}); + if (!result) { + return null; + } else if (!result.stdout) { + return null; + } else { + const out = result.stdout.toString("utf-8").trim(); + return out; + } +}; + +const getEsyVariable = (workingDirectory, variableName) => { + return runEsyCommand(workingDirectory, ["echo", "#{" + variableName +"}"]); +}; + +const isBuilt = (workingDirectory) => { + const json = runEsyCommand(workingDirectory, ["status"]); + if (!json) { + return false; + } + + return JSON.parse(json).isProjectReadyForDev; +}; + +const ensureInstalled = (workingDirectory) => { + runEsyCommand(workingDirectory, ["x", "echo"]); +}; + +module.exports = { + ensureInstalled, + getEsyVariable, + isBuilt, +}; diff --git a/src/package-darwin.js b/src/package-darwin.js index c580f2d..1c1fd87 100644 --- a/src/package-darwin.js +++ b/src/package-darwin.js @@ -1,142 +1,140 @@ -const fs = require("fs-extra"); -const path = require("path"); -const appdmg = require("appdmg"); - -const util = require("./util"); -const esy = require("./esy"); - -const makeDmg = async (spec) => { - return new Promise((resolve, reject) => { - console.dir(spec); - const ee = appdmg(spec); - - ee.on('progress', (info) => { - if (info.type == 'step-begin') { - console.log("[DMG] " + info.title); - } - }); - - ee.on('finish', () => { - resolve(); - }); - - ee.on('error', (err) => { - reject(err); - }); - }); -}; - -module.exports = async (config) => { - const appName = config.bundleInfo.bundleName + ".App"; - - console.log("Packaging for OSX: " + appName); - - // The MacOS app folder structure looks like this: - // - MyApp.App - // - Contents - // - Resources (non-executable resources) - // - MacOS (executable reosurces) - // - Frameworks (library code) - - const appDirectory = path.join(config.platformReleaseDir, appName); - const contentsDirectory = path.join(appDirectory, "Contents"); - const resourcesDirectory = path.join(contentsDirectory, "Resources"); - const binaryDirectory = path.join(contentsDirectory, "MacOS"); - const frameworksDirectory = path.join(contentsDirectory, "Frameworks"); - - const plistFile = path.join(contentsDirectory, "Info.plist"); - - fs.mkdirpSync(frameworksDirectory); - fs.mkdirpSync(resourcesDirectory); - - const bundleInfo = config.bundleInfo; - - // Create an Info.plist file for the app - const plistContents = { - CFBundleName: bundleInfo.bundleName, - CFBundleDisplayName: bundleInfo.displayName, - CFBundleIdentifier: bundleInfo.bundleId, - CFBundleIconFile: bundleInfo.iconFile, - CFBundleVersion: config.packageInfo.version, - CFBundlePackageType: "APPL", - CFBundleSignature: "????", - CFBundleExecutable: bundleInfo.mainExecutable, - NSHighResolutionCapable: true, - }; - - fs.writeFileSync(plistFile, require("plist").build(plistContents)); - - // Copy the bin folder over - util.copy(config.binPath, binaryDirectory); - - // ...but move non-executables from the bin folder to the resources folder, - // but symlink in, so that the executables can pretend they are available - - const filesToBeMoved = fs.readdirSync(binaryDirectory).filter((f) => { - return f != bundleInfo.mainExecutable; - }); - - filesToBeMoved.forEach((file) => { - console.log("Moving file: " + file); - const fileSrc = path.join(binaryDirectory, file); - const fileDest = path.join(resourcesDirectory, file); - console.log(`Moving file from ${fileSrc} to ${fileDest}.`); - fs.moveSync(fileSrc, fileDest); - const symlinkDest = path.join("../Resources", file); - console.log(`Symlinking ${symlinkDest} -> ${fileSrc}`); - fs.ensureSymlinkSync(symlinkDest, fileSrc); - }); - - console.log("Bundling dylibs..."); - - const executablePath = path.join(binaryDirectory, bundleInfo.mainExecutable); - - // Run the 'dylibbundler' tool - util.shell(`${config.macBundlerPath} -b -x "${executablePath}" -d "${frameworksDirectory}" -p "@executable_path/../Frameworks/" -cd`); - - // Bundle into tar package, if specified - if(config.bundleInfo.packages.indexOf("tar") >= 0) { - const tarDest = `${config.bundleInfo.bundleName}-darwin.tar.gz`; - util.shell(`cd '${config.platformReleaseDir}' && tar -zcf '../${tarDest}' ${appName}`); - console.log(`** Created tar package: ${tarDest}`); - } - - // Create DMG package, if specified - if(config.bundleInfo.packages.indexOf("dmg") >= 0) { - const dmgTarget = config.bundleInfo.bundleName + ".dmg"; - const spec = { - target: path.join(config.releaseDir, dmgTarget), - basepath: config.platformReleaseDir, - specification: { - title: config.bundleInfo.displayName, - background: config.bundleInfo.dmgBackground, - format: "ULFO", - window: { - size: { - width: 660, - height: 400, - } - }, - contents: [ - { - x: 180, - y: 170, - type: "file", - path: appDirectory, - }, - { - x: 480, - y: 170, - type: "link", - path: "/Applications" - } - ] - }, - }; - - await makeDmg(spec); - console.log("** Created DMG: " + dmgTarget); - } - - console.log("OSX packaging complete!"); -}; +const fs = require("fs-extra"); +const path = require("path"); +const appdmg = require("appdmg"); + +const util = require("./util"); +const esy = require("./esy"); + +const makeDmg = async (spec) => { + return new Promise((resolve, reject) => { + console.dir(spec); + const ee = appdmg(spec); + + ee.on('progress', (info) => { + if (info.type == 'step-begin') { + console.log("[DMG] " + info.title); + } + }); + + ee.on('finish', () => { + resolve(); + }); + + ee.on('error', (err) => { + reject(err); + }); + }); +}; + +module.exports = async (config) => { + const appName = config.bundleInfo.bundleName + ".App"; + + console.log("Packaging for OSX: " + appName); + + // The MacOS app folder structure looks like this: + // - MyApp.App + // - Contents + // - Resources (non-executable resources) + // - MacOS (executable reosurces) + // - Frameworks (library code) + const appDirectory = path.join(config.platformReleaseDir, appName); + const contentsDirectory = path.join(appDirectory, "Contents"); + const resourcesDirectory = path.join(contentsDirectory, "Resources"); + const binaryDirectory = path.join(contentsDirectory, "MacOS"); + const frameworksDirectory = path.join(contentsDirectory, "Frameworks"); + const plistFile = path.join(contentsDirectory, "Info.plist"); + + fs.mkdirpSync(frameworksDirectory); + fs.mkdirpSync(resourcesDirectory); + + const bundleInfo = config.bundleInfo; + + // Create an Info.plist file for the app + const plistContents = { + CFBundleName: bundleInfo.bundleName, + CFBundleDisplayName: bundleInfo.displayName, + CFBundleIdentifier: bundleInfo.bundleId, + CFBundleIconFile: bundleInfo.iconFile, + CFBundleVersion: config.packageInfo.version, + CFBundlePackageType: "APPL", + CFBundleSignature: "????", + CFBundleExecutable: bundleInfo.mainExecutable, + NSHighResolutionCapable: true, + }; + + fs.writeFileSync(plistFile, require("plist").build(plistContents)); + + // Copy the bin folder over + util.copy(config.binPath, binaryDirectory); + + // ...but move non-executables from the bin folder to the resources folder, + // but symlink in, so that the executables can pretend they are available + const filesToBeMoved = fs.readdirSync(binaryDirectory).filter((f) => { + return f != bundleInfo.mainExecutable; + }); + + filesToBeMoved.forEach((file) => { + console.log("Moving file: " + file); + const fileSrc = path.join(binaryDirectory, file); + const fileDest = path.join(resourcesDirectory, file); + console.log(`Moving file from ${fileSrc} to ${fileDest}.`); + fs.moveSync(fileSrc, fileDest); + + const symlinkDest = path.join("../Resources", file); + console.log(`Symlinking ${symlinkDest} -> ${fileSrc}`); + fs.ensureSymlinkSync(symlinkDest, fileSrc); + }); + + console.log("Bundling dylibs..."); + + const executablePath = path.join(binaryDirectory, bundleInfo.mainExecutable); + + // Run the 'dylibbundler' tool + util.shell(`${config.macBundlerPath} -b -x "${executablePath}" -d "${frameworksDirectory}" -p "@executable_path/../Frameworks/" -cd`); + + // Bundle into tar package, if specified + if(config.bundleInfo.packages.indexOf("tar") >= 0) { + const tarDest = `${config.bundleInfo.bundleName}-darwin.tar.gz`; + util.shell(`cd '${config.platformReleaseDir}' && tar -zcf '../${tarDest}' ${appName}`); + console.log(`** Created tar package: ${tarDest}`); + } + + // Create DMG package, if specified + if(config.bundleInfo.packages.indexOf("dmg") >= 0) { + const dmgTarget = config.bundleInfo.bundleName + ".dmg"; + const spec = { + target: path.join(config.releaseDir, dmgTarget), + basepath: config.platformReleaseDir, + specification: { + title: config.bundleInfo.displayName, + background: config.bundleInfo.dmgBackground, + format: "ULFO", + window: { + size: { + width: 660, + height: 400, + } + }, + contents: [ + { + x: 180, + y: 170, + type: "file", + path: appDirectory, + }, + { + x: 480, + y: 170, + type: "link", + path: "/Applications" + } + ] + }, + }; + + await makeDmg(spec); + console.log("** Created DMG: " + dmgTarget); + } + + console.log("OSX packaging complete!"); +}; diff --git a/src/package-linux.js b/src/package-linux.js index 73711f0..45be2da 100644 --- a/src/package-linux.js +++ b/src/package-linux.js @@ -1,100 +1,100 @@ -const fs = require("fs-extra"); -const os = require("os"); -const path = require("path"); - -const util = require("./util"); -const esy = require("./esy"); - -const desktopFile = (bundleInfo) => -`[Desktop Entry] -Name=${bundleInfo.bundleName} -Exec=${bundleInfo.mainExecutable} -Icon=Icon -Type=${bundleInfo.appImageType} -Categories=${bundleInfo.appImageCategory};`; - -const appRun = (bundleInfo) => { -const HERE = "${HERE}"; -return `#!/bin/sh -HERE=$(dirname $(readlink -f "\${0}")) -export PATH="${HERE}/usr/bin:$PATH" -export LD_LIBRARY_PATH="${HERE}/usr/lib/:$LD_LIBRARY_PATH" -"${HERE}"/usr/bin/${bundleInfo.mainExecutable} $@`; -} - -module.exports = async (config) => { - console.log("Packaging for linux."); - - // Create a temp folder so that we can download some extra tools: - // linuxdeploy - // appimagetool - - const tempFolder = path.join(os.tmpdir(), "revery-packager"); - fs.mkdirpSync(tempFolder); - - const linuxDeployAppImagePath = path.join(tempFolder, "linuxdeploy-x86_64.AppImage"); - - console.log(" - Installing linuxdeploy..."); - util.shell(`wget -O '${linuxDeployAppImagePath}' https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage`); - util.shell(`chmod +x '${linuxDeployAppImagePath}'`); - - console.log(" - Installing appimagetool..."); - const appImageToolPath = path.join(tempFolder, "appimagetool-x86_64.AppImage"); - util.shell(`wget -O '${appImageToolPath}' https://github.com/AppImage/AppImageKit/releases/download/12/appimagetool-x86_64.AppImage`); - util.shell(`chmod +x '${appImageToolPath}'`); - - const appDirName = config.bundleInfo.bundleName + ".AppDir"; - const appDirFolder = path.join(config.platformReleaseDir, appDirName); - - const binFolder = path.join(appDirFolder, "usr", "bin"); - - // Create a temporary staging folder - const staging = path.join(os.tmpdir(), "revery-packager-staging" + new Date().getTime().toString()); - - const stagingBin = path.join(staging, "bin"); - fs.mkdirpSync(stagingBin); - - // Copy binaries to staging, and run linux deploy - util.copy(config.binPath, stagingBin); - - // Create desktop and app run files - const appRunStagingPath = path.join(staging, "AppRun"); - const desktopFileName = config.bundleInfo.bundleName + ".desktop"; - const desktopStagingPath = path.join(staging, desktopFileName) - fs.writeFileSync(appRunStagingPath, appRun(config.bundleInfo), "utf8"); - fs.writeFileSync(desktopStagingPath, desktopFile(config.bundleInfo), "utf8"); - - const iconFilePath = path.join(staging, "Icon.png"); - util.copy(config.bundleInfo.iconFile, iconFilePath); - - util.copy(appRunStagingPath, path.join(appDirFolder, "AppRun")); - util.shell(`chmod +x '${path.join(appDirFolder, "AppRun")}'`); - - fs.mkdirpSync(appDirFolder); - fs.mkdirpSync(binFolder); - - const mainBinaryPath = path.join(stagingBin, config.bundleInfo.mainExecutable); - util.copy(stagingBin, binFolder); - // Run linuxdeploy on the app image binaries - util.shell(`${linuxDeployAppImagePath} -e '${mainBinaryPath}' --appdir '${appDirFolder}' -d '${desktopStagingPath}' -i '${iconFilePath}'`); - - util.copy(config.bundleInfo.iconFile, path.join(appDirFolder, "Icon.png")); - console.log("**Created app folder: " + appDirFolder); - - // Create tar - if(config.bundleInfo.packages.indexOf("tar") >= 0) { - const tarDest = `${config.bundleInfo.bundleName}-linux.tar.gz`; - util.shell(`cd '${config.platformReleaseDir}' && tar -zcf '../${tarDest}' ${appDirName}`); - console.log(`** Created tar package: ${tarDest}`); - } - - // Create app image - if(config.bundleInfo.packages.indexOf("appimage") >= 0) { - const appImageDest = path.join(config.releaseDir, `${config.bundleInfo.bundleName}-x86_64.AppImage`); - util.shell(`ARCH=x86_64 ${appImageToolPath} '${appDirFolder}' '${appImageDest}'`); - console.log(`** Created appImage: ${appImageDest}`); - } - - // Clean up - fs.removeSync(tempFolder); -}; +const fs = require("fs-extra"); +const os = require("os"); +const path = require("path"); + +const util = require("./util"); +const esy = require("./esy"); + +const desktopFile = (bundleInfo) => +`[Desktop Entry] +Name=${bundleInfo.bundleName} +Exec=${bundleInfo.mainExecutable} +Icon=Icon +Type=${bundleInfo.appImageType} +Categories=${bundleInfo.appImageCategory};`; + +const appRun = (bundleInfo) => { +const HERE = "${HERE}"; +return `#!/bin/sh +HERE=$(dirname $(readlink -f "\${0}")) +export PATH="${HERE}/usr/bin:$PATH" +export LD_LIBRARY_PATH="${HERE}/usr/lib/:$LD_LIBRARY_PATH" +"${HERE}"/usr/bin/${bundleInfo.mainExecutable} $@`; +} + +module.exports = async (config) => { + console.log("Packaging for linux."); + + // Create a temp folder so that we can download some extra tools: + // linuxdeploy + // appimagetool + + const tempFolder = path.join(os.tmpdir(), "revery-packager"); + fs.mkdirpSync(tempFolder); + + const linuxDeployAppImagePath = path.join(tempFolder, "linuxdeploy-x86_64.AppImage"); + + console.log(" - Installing linuxdeploy..."); + util.shell(`wget -O '${linuxDeployAppImagePath}' https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage`); + util.shell(`chmod +x '${linuxDeployAppImagePath}'`); + + console.log(" - Installing appimagetool..."); + const appImageToolPath = path.join(tempFolder, "appimagetool-x86_64.AppImage"); + util.shell(`wget -O '${appImageToolPath}' https://github.com/AppImage/AppImageKit/releases/download/12/appimagetool-x86_64.AppImage`); + util.shell(`chmod +x '${appImageToolPath}'`); + + const appDirName = config.bundleInfo.bundleName + ".AppDir"; + const appDirFolder = path.join(config.platformReleaseDir, appDirName); + + const binFolder = path.join(appDirFolder, "usr", "bin"); + + // Create a temporary staging folder + const staging = path.join(os.tmpdir(), "revery-packager-staging" + new Date().getTime().toString()); + + const stagingBin = path.join(staging, "bin"); + fs.mkdirpSync(stagingBin); + + // Copy binaries to staging, and run linux deploy + util.copy(config.binPath, stagingBin); + + // Create desktop and app run files + const appRunStagingPath = path.join(staging, "AppRun"); + const desktopFileName = config.bundleInfo.bundleName + ".desktop"; + const desktopStagingPath = path.join(staging, desktopFileName) + fs.writeFileSync(appRunStagingPath, appRun(config.bundleInfo), "utf8"); + fs.writeFileSync(desktopStagingPath, desktopFile(config.bundleInfo), "utf8"); + + const iconFilePath = path.join(staging, "Icon.png"); + util.copy(config.bundleInfo.iconFile, iconFilePath); + + util.copy(appRunStagingPath, path.join(appDirFolder, "AppRun")); + util.shell(`chmod +x '${path.join(appDirFolder, "AppRun")}'`); + + fs.mkdirpSync(appDirFolder); + fs.mkdirpSync(binFolder); + + const mainBinaryPath = path.join(stagingBin, config.bundleInfo.mainExecutable); + util.copy(stagingBin, binFolder); + // Run linuxdeploy on the app image binaries + util.shell(`${linuxDeployAppImagePath} -e '${mainBinaryPath}' --appdir '${appDirFolder}' -d '${desktopStagingPath}' -i '${iconFilePath}'`); + + util.copy(config.bundleInfo.iconFile, path.join(appDirFolder, "Icon.png")); + console.log("**Created app folder: " + appDirFolder); + + // Create tar + if(config.bundleInfo.packages.indexOf("tar") >= 0) { + const tarDest = `${config.bundleInfo.bundleName}-linux.tar.gz`; + util.shell(`cd '${config.platformReleaseDir}' && tar -zcf '../${tarDest}' ${appDirName}`); + console.log(`** Created tar package: ${tarDest}`); + } + + // Create app image + if(config.bundleInfo.packages.indexOf("appimage") >= 0) { + const appImageDest = path.join(config.releaseDir, `${config.bundleInfo.bundleName}-x86_64.AppImage`); + util.shell(`ARCH=x86_64 ${appImageToolPath} '${appDirFolder}' '${appImageDest}'`); + console.log(`** Created appImage: ${appImageDest}`); + } + + // Clean up + fs.removeSync(tempFolder); +}; diff --git a/src/package-windows.js b/src/package-windows.js index f575d56..530f07e 100644 --- a/src/package-windows.js +++ b/src/package-windows.js @@ -1,33 +1,32 @@ -const fs = require("fs-extra"); -const path = require("path"); -const {zip} = require("zip-a-folder"); - -const util = require("./util"); -const esy = require("./esy"); - -module.exports = async (config) => { - console.log("Packaging for windows."); - - util.copy(config.binPath, config.platformReleaseDir); - - // We need to bring over MingW runtime dlls - console.log("Copying runtime DLLs"); - const filesToBeMoved = fs.readdirSync(config.reveryBinPath).filter((f) => { - return path.extname(f) == ".dll"; - }); - - filesToBeMoved.forEach((f) => { - util.copy(path.join(config.reveryBinPath, f), path.join(config.binPath)); - }); - - - if(config.bundleInfo.packages.indexOf("zip") >= 0) { - // Create zip - const fileName = `${config.bundleInfo.bundleName}-win32-x64.zip`; - const zipDest = path.join(config.releaseDir, fileName); - - await zip(config.platformReleaseDir, zipDest); - - console.log("** Created zip: " + zipDest); - }; -}; +const fs = require("fs-extra"); +const path = require("path"); +const {zip} = require("zip-a-folder"); + +const util = require("./util"); +const esy = require("./esy"); + +module.exports = async (config) => { + console.log("Packaging for windows."); + + util.copy(config.binPath, config.platformReleaseDir); + + // We need to bring over MingW runtime dlls + console.log("Copying runtime DLLs"); + const filesToBeMoved = fs.readdirSync(config.reveryBinPath).filter((f) => { + return path.extname(f) == ".dll"; + }); + + filesToBeMoved.forEach((f) => { + util.copy(path.join(config.reveryBinPath, f), path.join(config.binPath)); + }); + + if(config.bundleInfo.packages.indexOf("zip") >= 0) { + // Create zip + const fileName = `${config.bundleInfo.bundleName}-win32-x64.zip`; + const zipDest = path.join(config.releaseDir, fileName); + + await zip(config.platformReleaseDir, zipDest); + + console.log("** Created zip: " + zipDest); + }; +}; diff --git a/src/util.js b/src/util.js index f33f609..dc9f2f8 100644 --- a/src/util.js +++ b/src/util.js @@ -1,24 +1,24 @@ -const cp = require("child_process"); -const fs = require("fs-extra"); - -const copy = (source, dest) => { - console.log(`Copying from ${source} to ${dest}`); - if (process.platform == "darwin") { - shell(`cp -r "${source}" "${dest}"`) - } else { - fs.copySync(source, dest, {dereference: true}); - } - console.log("Successfully copied."); -}; - -const shell = (cmd) => { - console.log(`[shell] ${cmd}`); - const out = cp.execSync(cmd); - console.log(`[shell - output]: ${out.toString("utf8")}`); - return out.toString("utf8"); -}; - -module.exports = { - copy, - shell, -}; +const cp = require("child_process"); +const fs = require("fs-extra"); + +const copy = (source, dest) => { + console.log(`Copying from ${source} to ${dest}`); + if (process.platform == "darwin") { + shell(`cp -r "${source}" "${dest}"`) + } else { + fs.copySync(source, dest, {dereference: true}); + } + console.log("Successfully copied."); +}; + +const shell = (cmd) => { + console.log(`[shell] ${cmd}`); + const out = cp.execSync(cmd); + console.log(`[shell - output]: ${out.toString("utf8")}`); + return out.toString("utf8"); +}; + +module.exports = { + copy, + shell, +};