publish_xmgame.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495
  1. // v1.1.0
  2. // publish 2.x 也是用这个文件,需要做兼容
  3. let isPublish2 = process.argv[2].includes("publish_xmgame.js") && process.argv[3].includes("--evn=publish2");
  4. // 获取Node插件和工作路径
  5. let ideModuleDir, workSpaceDir;
  6. if (isPublish2) {
  7. //是否使用IDE自带的node环境和插件,设置false后,则使用自己环境(使用命令行方式执行)
  8. const useIDENode = process.argv[0].indexOf("LayaAir") > -1 ? true : false;
  9. ideModuleDir = useIDENode ? process.argv[1].replace("gulp\\bin\\gulp.js", "").replace("gulp/bin/gulp.js", "") : "";
  10. workSpaceDir = useIDENode ? process.argv[2].replace("--gulpfile=", "").replace("\\.laya\\publish_xmgame.js", "").replace("/.laya/publish_xmgame.js", "") + "/" : "./../";
  11. } else {
  12. ideModuleDir = global.ideModuleDir;
  13. workSpaceDir = global.workSpaceDir;
  14. }
  15. //引用插件模块
  16. const gulp = require(ideModuleDir + "gulp");
  17. const fs = require("fs");
  18. const path = require("path");
  19. const childProcess = require("child_process");
  20. const del = require(ideModuleDir + "del");
  21. let commandSuffix = ".cmd";
  22. let prevTasks = ["packfile"];
  23. if (isPublish2) {
  24. prevTasks = "";
  25. }
  26. let
  27. config,
  28. platform,
  29. releaseDir,
  30. tempReleaseDir, // 小米临时拷贝目录
  31. projDir; // 小米快游戏工程目录
  32. let IDEXMProjPath,
  33. isUpdateIDEXMProj = false;
  34. // 创建小米项目前,拷贝小米引擎库、修改index.js
  35. // 应该在publish中的,但是为了方便发布2.0及IDE 1.x,放在这里修改
  36. gulp.task("preCreate_XM", prevTasks, function() {
  37. if (isPublish2) {
  38. let pubsetPath = path.join(workSpaceDir, ".laya", "pubset.json");
  39. let content = fs.readFileSync(pubsetPath, "utf8");
  40. let pubsetJson = JSON.parse(content);
  41. platform = "xmgame";
  42. releaseDir = path.join(workSpaceDir, "release", platform).replace(/\\/g, "/");
  43. releaseDir = tempReleaseDir = path.join(releaseDir, "temprelease");
  44. config = pubsetJson[4]; // 只用到了 config.xmInfo|config.xmSign
  45. } else {
  46. platform = global.platform;
  47. releaseDir = global.releaseDir;
  48. tempReleaseDir = global.tempReleaseDir;
  49. config = global.config;
  50. }
  51. // 如果不是小米快游戏
  52. if (platform !== "xmgame") {
  53. return;
  54. }
  55. if (process.platform === "darwin") {
  56. commandSuffix = "";
  57. }
  58. let copyLibsList = [`${workSpaceDir}/bin/libs/laya.xmmini.js`];
  59. var stream = gulp.src(copyLibsList, { base: `${workSpaceDir}/bin` });
  60. return stream.pipe(gulp.dest(tempReleaseDir));
  61. });
  62. gulp.task("copyPlatformFile_XM", ["preCreate_XM"], function() {
  63. // 如果不是小米快游戏
  64. if (platform !== "xmgame") {
  65. return;
  66. }
  67. let xmAdapterPath = path.join(ideModuleDir, "../", "out", "layarepublic", "LayaAirProjectPack", "lib", "data", "xmfiles");
  68. let copyLibsList = [`${xmAdapterPath}/**/*.*`];
  69. var stream = gulp.src(copyLibsList);
  70. return stream.pipe(gulp.dest(tempReleaseDir));
  71. });
  72. // 新建小米项目-小米项目与其他项目不同,需要新建小米快游戏项目,并打包成.rpk文件
  73. gulp.task("checkIDEProj_XM", ["copyPlatformFile_XM"], function() {
  74. // 如果不是小米快游戏
  75. if (platform !== "xmgame") {
  76. return;
  77. }
  78. if (!ideModuleDir) {
  79. return;
  80. }
  81. IDEXMProjPath = path.join(ideModuleDir, "../", "out", "layarepublic", "xm");
  82. if (process.platform === "darwin") {
  83. return;
  84. }
  85. let ideLastXMProjPath = path.join(IDEXMProjPath, config.xmInfo.projName);
  86. // 如果IDE中没有小米项目,跳过这一步
  87. let isProjExist = fs.existsSync(ideLastXMProjPath + "/node_modules") &&
  88. fs.existsSync(ideLastXMProjPath + "/sign");
  89. if (!isProjExist) {
  90. console.log("IDE中没有小米项目,跳过检查小米项目版本号这一步");
  91. return;
  92. }
  93. // 如果IDE中项目已经存在了,检查版本号
  94. // npm view quickgame-cli version
  95. // npm ls quickgame-cli
  96. let remoteVersion, localVersion;
  97. let isGetRemote, isGetLocal;
  98. return new Promise((resolve, reject) => { // 远程版本号
  99. childProcess.exec("npm view quickgame-cli version", function(error, stdout, stderr) {
  100. if (!stdout) { // 获取 quickgame-cli 远程版本号失败
  101. reject();
  102. return;
  103. }
  104. remoteVersion = stdout;
  105. isGetRemote = true;
  106. if (isGetRemote && isGetLocal) {
  107. resolve();
  108. }
  109. });
  110. childProcess.exec("npm ls quickgame-cli", { cwd: ideLastXMProjPath }, function(error, stdout, stderr) {
  111. if (!stdout) { // 获取 quickgame-cli 本地版本号失败
  112. reject();
  113. return;
  114. }
  115. localVersion = stdout.match(/quickgame-cli@(.+)/);
  116. localVersion = localVersion && localVersion[1];
  117. isGetLocal = true;
  118. if (isGetRemote && isGetLocal) {
  119. resolve();
  120. }
  121. });
  122. setTimeout(() => {
  123. if (!isGetLocal || !isGetRemote) {
  124. console.log("获取远程版本号或本地版本号失败");
  125. reject();
  126. return;
  127. }
  128. }, 10000);
  129. }).then(() => { // 比较两个版本号
  130. if (!remoteVersion || !localVersion) {
  131. console.log("获取远程版本号或本地版本号失败!");
  132. }
  133. console.log("quickgame-cli -> ", localVersion, "|", remoteVersion);
  134. if (remoteVersion.trim() !== localVersion.trim()) { // 仅当两个版本号都获取到并且不相等,置为需要更新(true)
  135. isUpdateIDEXMProj = true;
  136. }
  137. }).catch((e) => {
  138. console.log("获取远程版本号或本地版本号失败 -> ", remoteVersion, "|", localVersion);
  139. console.log(e);
  140. });
  141. });
  142. gulp.task("createIDEProj_XM", ["checkIDEProj_XM"], function() {
  143. // 如果不是小米快游戏
  144. if (platform !== "xmgame") {
  145. return;
  146. }
  147. if (!ideModuleDir) {
  148. return;
  149. }
  150. if (process.platform === "darwin") {
  151. return;
  152. }
  153. let ideLastXMProjPath = path.join(IDEXMProjPath, config.xmInfo.projName);
  154. // 如果有即存项目,不再新建
  155. let isProjExist = fs.existsSync(ideLastXMProjPath + "/node_modules") &&
  156. fs.existsSync(ideLastXMProjPath + "/sign");
  157. if (isProjExist && !isUpdateIDEXMProj) { // 项目存在并且不需要更新IDE中的小米项目
  158. return;
  159. }
  160. return new Promise((resolve, reject) => {
  161. console.log("(IDE)开始创建小米快游戏项目,请耐心等待(预计需要10分钟)...");
  162. let cmd = `npx${commandSuffix}`;
  163. let args = ["create-quickgame", config.xmInfo.projName, `path=${IDEXMProjPath}`,
  164. `package=${config.xmInfo.package}`, `versionName=${config.xmInfo.versionName}`,
  165. `versionCode=${config.xmInfo.versionCode}`, `minPlatformVersion=${config.xmInfo.minPlatformVersion}`,
  166. `icon=/layaicon/${path.basename(config.xmInfo.icon)}`, `name=${config.xmInfo.name}`, `rebuild=true`];
  167. console.log(JSON.stringify(args));
  168. let cp = childProcess.spawn(cmd, args);
  169. cp.stdout.on('data', (data) => {
  170. console.log(`stdout: ${data}`);
  171. });
  172. cp.stderr.on('data', (data) => {
  173. console.log(`stderr: ${data}`);
  174. // reject();
  175. });
  176. cp.on('close', (code) => {
  177. console.log(`子进程退出码:${code}`);
  178. resolve();
  179. });
  180. });
  181. });
  182. gulp.task("createProj_XM", ["createIDEProj_XM"], function() {
  183. // 如果不是小米快游戏
  184. if (platform !== "xmgame") {
  185. return;
  186. }
  187. releaseDir = path.dirname(releaseDir);
  188. projDir = path.join(releaseDir, config.xmInfo.projName);
  189. // 如果有即存项目,不再新建
  190. let isProjExist = fs.existsSync(projDir + "/node_modules") &&
  191. fs.existsSync(projDir + "/sign");
  192. if (isProjExist) {
  193. return;
  194. }
  195. // 如果IDE中有即存项目,不再新建,从IDE中拷贝
  196. let ideLastXMProjPath = path.join(IDEXMProjPath, config.xmInfo.projName);
  197. let isIDEXMProjExist = fs.existsSync(ideLastXMProjPath + "/node_modules") &&
  198. fs.existsSync(ideLastXMProjPath + "/sign");
  199. if (isIDEXMProjExist) { // 如果用的IDE并且有IDEXM目录
  200. console.log("使用IDE中的小米游戏项目,拷贝...");
  201. // node-glob语法中,* 无法匹配 .开头的文件(夹),必须手动匹配
  202. let IDEXMProjPathStr = [`${IDEXMProjPath}/**/*.*`, `${ideLastXMProjPath}/node_modules/.bin/*.*`];
  203. var stream = gulp.src(IDEXMProjPathStr, { base: IDEXMProjPath});
  204. return stream.pipe(gulp.dest(releaseDir));
  205. }
  206. // 在项目中创建小米项目
  207. return new Promise((resolve, reject) => {
  208. console.log("(proj)开始创建小米快游戏项目,请耐心等待(预计需要10分钟)...");
  209. let cmd = `npx${commandSuffix}`;
  210. let args = ["create-quickgame", config.xmInfo.projName, `path=${releaseDir}`,
  211. `package=${config.xmInfo.package}`, `versionName=${config.xmInfo.versionName}`,
  212. `versionCode=${config.xmInfo.versionCode}`, `minPlatformVersion=${config.xmInfo.minPlatformVersion}`,
  213. `icon=/layaicon/${path.basename(config.xmInfo.icon)}`, `name=${config.xmInfo.name}`, `rebuild=true`];
  214. console.log(JSON.stringify(args));
  215. let cp = childProcess.spawn(cmd, args);
  216. cp.stdout.on('data', (data) => {
  217. console.log(`stdout: ${data}`);
  218. });
  219. cp.stderr.on('data', (data) => {
  220. console.log(`stderr: ${data}`);
  221. // reject();
  222. });
  223. cp.on('close', (code) => {
  224. console.log(`子进程退出码:${code}`);
  225. resolve();
  226. });
  227. });
  228. });
  229. // 拷贝文件到小米快游戏
  230. gulp.task("copyFileToProj_XM", ["createProj_XM"], function() {
  231. // 如果不是小米快游戏
  232. if (platform !== "xmgame") {
  233. return;
  234. }
  235. // 将临时文件夹中的文件,拷贝到项目中去
  236. let originalDir = `${tempReleaseDir}/**/*.*`;
  237. let stream = gulp.src(originalDir);
  238. return stream.pipe(gulp.dest(path.join(projDir)));
  239. });
  240. // 拷贝icon到小米快游戏
  241. gulp.task("copyIconToProj_XM", ["copyFileToProj_XM"], function() {
  242. // 如果不是小米快游戏
  243. if (platform !== "xmgame") {
  244. return;
  245. }
  246. let originalDir = config.xmInfo.icon;
  247. let stream = gulp.src(originalDir);
  248. return stream.pipe(gulp.dest(path.join(projDir, "layaicon")));
  249. });
  250. // 清除小米快游戏临时目录
  251. gulp.task("clearTempDir_XM", ["copyIconToProj_XM"], function() {
  252. // 如果不是小米快游戏
  253. if (platform !== "xmgame") {
  254. return;
  255. }
  256. // 删掉临时目录
  257. return del([tempReleaseDir], { force: true });
  258. });
  259. // 生成release签名(私钥文件 private.pem 和证书文件 certificate.pem )
  260. gulp.task("generateSign_XM", ["clearTempDir_XM"], function() {
  261. // 如果不是小米快游戏
  262. if (platform !== "xmgame") {
  263. return;
  264. }
  265. if (!config.xmSign.generateSign) {
  266. return;
  267. }
  268. // https://doc.quickapp.cn/tools/compiling-tools.html
  269. return new Promise((resolve, reject) => {
  270. let cmd = "openssl";
  271. let args = ["req", "-newkey", "rsa:2048", "-nodes", "-keyout", "private.pem",
  272. "-x509", "-days", "3650", "-out", "certificate.pem"];
  273. let opts = {
  274. cwd: projDir,
  275. shell: true
  276. };
  277. let cp = childProcess.spawn(cmd, args, opts);
  278. cp.stdout.on('data', (data) => {
  279. console.log(`stdout: ${data}`);
  280. });
  281. cp.stderr.on('data', (data) => {
  282. console.log(`stderr: ${data}`);
  283. data += "";
  284. if (data.includes("Country Name")) {
  285. cp.stdin.write(`${config.xmSign.countryName}\n`);
  286. console.log(`Country Name: ${config.xmSign.countryName}`);
  287. } else if (data.includes("Province Name")) {
  288. cp.stdin.write(`${config.xmSign.provinceName}\n`);
  289. console.log(`Province Name: ${config.xmSign.provinceName}`);
  290. } else if (data.includes("Locality Name")) {
  291. cp.stdin.write(`${config.xmSign.localityName}\n`);
  292. console.log(`Locality Name: ${config.xmSign.localityName}`);
  293. } else if (data.includes("Organization Name")) {
  294. cp.stdin.write(`${config.xmSign.orgName}\n`);
  295. console.log(`Organization Name: ${config.xmSign.orgName}`);
  296. } else if (data.includes("Organizational Unit Name")) {
  297. cp.stdin.write(`${config.xmSign.orgUnitName}\n`);
  298. console.log(`Organizational Unit Name: ${config.xmSign.orgUnitName}`);
  299. } else if (data.includes("Common Name")) {
  300. cp.stdin.write(`${config.xmSign.commonName}\n`);
  301. console.log(`Common Name: ${config.xmSign.commonName}`);
  302. } else if (data.includes("Email Address")) {
  303. cp.stdin.write(`${config.xmSign.emailAddr}\n`);
  304. console.log(`Email Address: ${config.xmSign.emailAddr}`);
  305. // cp.stdin.end();
  306. }
  307. // reject();
  308. });
  309. cp.on('close', (code) => {
  310. console.log(`子进程退出码:${code}`);
  311. resolve();
  312. });
  313. });
  314. });
  315. // 拷贝sign文件到指定位置
  316. gulp.task("copySignFile_XM", ["generateSign_XM"], function() {
  317. // 如果不是小米快游戏
  318. if (platform !== "xmgame") {
  319. return;
  320. }
  321. if (config.xmSign.generateSign) { // 新生成的签名
  322. // 移动签名文件到项目中(Laya & 小米快游戏项目中)
  323. let
  324. privatePem = path.join(projDir, "private.pem"),
  325. certificatePem = path.join(projDir, "certificate.pem");
  326. let isSignExits = fs.existsSync(privatePem) && fs.existsSync(certificatePem);
  327. if (!isSignExits) {
  328. return;
  329. }
  330. let
  331. xiaomiDest = `${projDir}/sign/release`,
  332. layaDest = `${workSpaceDir}/sign/release`;
  333. let stream = gulp.src([privatePem, certificatePem]);
  334. return stream.pipe(gulp.dest(xiaomiDest))
  335. .pipe(gulp.dest(layaDest));
  336. } else if (config.xmInfo.useReleaseSign && !config.xmSign.generateSign) { // 使用release签名,并且没有重新生成
  337. // 从项目中将签名拷贝到小米快游戏项目中
  338. let
  339. privatePem = path.join(workSpaceDir, "sign", "release", "private.pem"),
  340. certificatePem = path.join(workSpaceDir, "sign", "release", "certificate.pem");
  341. let isSignExits = fs.existsSync(privatePem) && fs.existsSync(certificatePem);
  342. if (!isSignExits) {
  343. return;
  344. }
  345. let
  346. xiaomiDest = `${projDir}/sign/release`;
  347. let stream = gulp.src([privatePem, certificatePem]);
  348. return stream.pipe(gulp.dest(xiaomiDest));
  349. }
  350. });
  351. gulp.task("deleteSignFile_XM", ["copySignFile_XM"], function() {
  352. // 如果不是小米快游戏
  353. if (platform !== "xmgame") {
  354. return;
  355. }
  356. if (config.xmSign.generateSign) { // 新生成的签名
  357. let
  358. privatePem = path.join(projDir, "private.pem"),
  359. certificatePem = path.join(projDir, "certificate.pem");
  360. return del([privatePem, certificatePem], { force: true });
  361. }
  362. });
  363. gulp.task("modifyFile_XM", ["deleteSignFile_XM"], function() {
  364. // 如果不是小米快游戏
  365. if (platform !== "xmgame") {
  366. return;
  367. }
  368. // 修改manifest.json文件
  369. let manifestPath = path.join(projDir, "manifest.json");
  370. if (!fs.existsSync(manifestPath)) {
  371. return;
  372. }
  373. let manifestContent = fs.readFileSync(manifestPath, "utf8");
  374. let manifestJson = JSON.parse(manifestContent);
  375. manifestJson.package = config.xmInfo.package;
  376. manifestJson.name = config.xmInfo.name;
  377. manifestJson.orientation = config.xmInfo.orientation;
  378. manifestJson.versionName = config.xmInfo.versionName;
  379. manifestJson.versionCode = config.xmInfo.versionCode;
  380. manifestJson.minPlatformVersion = config.xmInfo.minPlatformVersion;
  381. manifestJson.icon = `/layaicon/${path.basename(config.xmInfo.icon)}`;
  382. fs.writeFileSync(manifestPath, JSON.stringify(manifestJson, null, 4), "utf8");
  383. // 修改main.js文件
  384. let content = 'require("./qg-adapter.js");\nrequire("./libs/laya.xmmini.js");\nrequire("./index.js");';
  385. let mainJsPath = path.join(projDir, "main.js");
  386. fs.writeFileSync(mainJsPath, content, "utf8");
  387. // 小米项目,修改index.js
  388. let filePath = path.join(projDir, "index.js");
  389. if (!fs.existsSync(filePath)) {
  390. return;
  391. }
  392. let fileContent = fs.readFileSync(filePath, "utf8");
  393. fileContent = fileContent.replace(/loadLib(\(['"])/gm, "require$1./");
  394. fs.writeFileSync(filePath, fileContent, "utf8");
  395. })
  396. // 打包rpk
  397. gulp.task("buildRPK_XM", ["modifyFile_XM"], function() {
  398. // 如果不是小米快游戏
  399. if (platform !== "xmgame") {
  400. return;
  401. }
  402. // 在小米轻游戏项目目录中执行:
  403. // npm run build || npm run release
  404. let cmdStr = "build";
  405. if (config.xmInfo.useReleaseSign) {
  406. cmdStr = "release";
  407. }
  408. return new Promise((resolve, reject) => {
  409. let cmd = `npm${commandSuffix}`;
  410. let args = ["run", cmdStr];
  411. let opts = {
  412. cwd: projDir
  413. };
  414. let cp = childProcess.spawn(cmd, args, opts);
  415. // let cp = childProcess.spawn(`npx${commandSuffix}`, ['-v']);
  416. cp.stdout.on('data', (data) => {
  417. console.log(`stdout: ${data}`);
  418. });
  419. cp.stderr.on('data', (data) => {
  420. console.log(`stderr: ${data}`);
  421. // reject();
  422. });
  423. cp.on('close', (code) => {
  424. console.log(`子进程退出码:${code}`);
  425. resolve();
  426. });
  427. });
  428. });
  429. gulp.task("showQRCode_XM", ["buildRPK_XM"], function() {
  430. // 如果不是小米快游戏
  431. if (platform !== "xmgame") {
  432. return;
  433. }
  434. // 在小米轻游戏项目目录中执行:
  435. // npm run server
  436. return new Promise((resolve, reject) => {
  437. let cmd = `npm${commandSuffix}`;
  438. let args = ["run", "server"];
  439. let opts = {
  440. cwd: projDir
  441. };
  442. let cp = childProcess.spawn(cmd, args, opts);
  443. // let cp = childProcess.spawn(`npx${commandSuffix}`, ['-v']);
  444. cp.stdout.on('data', (data) => {
  445. console.log(`${data}`);
  446. // 输出pid,macos要用: macos无法kill进程树,也无法执行命令获取3000端口pid(没有查询权限),导致无法kill这个进程
  447. console.log('xm_qrcode_pid:' + cp.pid);
  448. });
  449. cp.stderr.on('data', (data) => {
  450. console.log(`stderr: ${data}`);
  451. // reject();
  452. });
  453. cp.on('close', (code) => {
  454. console.log(`子进程退出码:${code}`);
  455. resolve();
  456. });
  457. });
  458. });
  459. gulp.task("buildXiaomiProj", ["showQRCode_XM"], function() {
  460. console.log("all tasks completed");
  461. });