Caxa:包节点。将js应用程序转换为可执行二进制文件

2022-02-20 19:37:01

简单易用。npm安装caxa并从命令行调用caxa。无需声明要包含哪些文件;无需将应用程序捆绑到单个文件中。

支持任何类型的节点。js项目,包括那些带有本机模块的项目(例如,sharp、@leafac/sqlite(无耻的插件!),和其他人)。

相对较小的二进制文件。“你好,世界!”应用程序的容量约为30MB,与Go的2MB相比非常糟糕,与C的50KB相比更糟糕,但与Node的其他打包解决方案相比,它是同类中最好的。js。

没有魔法。不需要遍历require()以查找要包含的文件;节点没有补丁。js来源。

不支持交叉编译(例如,从macOS开发机器构建Windows可执行文件)。

不支持使用节点打包。js版本与运行caxa的版本不同(例如,将Node.js 15捆绑在一起,同时将caxa与Node.js 14捆绑在一起)。

建筑例如,用tsc编译TypeScript,用webpack打包,以及任何你需要的东西来让项目准备好开始。通常,这是npm准备脚本中要用到的内容,因此前一点中的npm ci可能已经解决了这一点。

如果包中有不应包含的文件,请将其从目录中删除。例如,您可能希望删除。git目录。

您不需要npm重复数据消除——生产,因为caxa将在构建目录中为您完成这项工作。(否则,如果您尝试npm重复数据消除--生产,您将卸载caxa,它可能应该在devdependences中。)

建议您在持续集成服务器上运行caxa。(例如,GitHub操作对存储库进行浅层获取,因此删除.git目录变得微不足道,但您始终可以使用--exclude advanced选项来完成这项操作。)

$npx caxa——帮助用法:caxa[选项]<;命令>;包节点。将js应用程序转换为可执行二进制文件。参数:每次调用可执行文件时,命令将运行该命令,并将可选参数传递给该命令。路径必须是绝对的。“{caxa}}”占位符将替换运行包的文件夹。“node”可执行文件可在“{caxa}}/node_modules/”上找到。bin/节点'。使用双引号分隔命令和每个参数。选项:-V,--version输出版本号-i,--input<;输入>;要打包的输入目录-o、 --输出<;产量>;生成可执行文件的路径。在Windows上必须以“”结尾。exe'。在macOS中可能会以'结尾。“应用程序”生成macOS应用程序包。在macOS和Linux中,可能以'结尾。sh’使用Shell Stub,占用的空间较小,但取决于最终用户机器上安装的一些工具,例如“tar”、“tail”等等-f、 --强制[高级]覆盖输出(如果存在)。(默认值:true)-F,-no force-e,--exclude<;路径>;[高级]从生成中排除的路径。路径被传递到https://github.com/sindresorhus/globby匹配的路径将被排除。[超级高级,请不要使用]如果你想模仿'-include',你可以使用'-exclude"*" ".*" "!包含";…'的路径。“--include”的问题是,如果您更改了项目结构,但忘记了更改caxa调用,那么只有在打包的版本中,事情才会微妙地失败-d、 --重复数据消除[高级]在生成目录上运行“npm dedupe--production”。(默认值:true)-D,--无重复数据消除-p,--prepare命令<;指挥部>;打包时在生成目录上运行的[Advanced]命令-n、 --包含节点[高级]复制节点。js可执行文件到{caxa}/node_modules/。bin/节点'。(默认值:true)-N,--no include节点-s,--stub<;路径>;[Advanced]指向存根的路径--标识符<;标识符>;[Advanced]构建标识符,即应用程序解包的路径-b、 --删除生成目录[Advanced]在生成后删除生成目录。(默认值:true)-B,-no remove build directory-m,--uncompression message<;信息>;[高级]解压时显示的消息,例如,“第一次运行可能需要一段时间,请稍候……”-h、 --帮助显示命令示例的帮助:Windows:>;caxa——输入和#34;示例/echo命令行参数"--输出";echo命令行参数。exe";——"{{caxa}}/node_modules/。bin/node""{{caxa}}/index。js""有些""嵌入参数""--这是命令的一部分#34;macOS/Linux:$caxa——输入和#34;示例/echo命令行参数"--输出";echo命令行参数";——"{{caxa}}/node_modules/。bin/node""{{caxa}}/index。js""有些""嵌入参数""--这是命令的一部分#34;macOS(应用程序包):$caxa——输入和#34;示例/echo命令行参数"--输出";Echo命令行参数。应用程序";——"{{caxa}}/node_modules/。bin/node""{{caxa}}/index。js""有些""嵌入参数""--这是命令的一部分#34;macOS/Linux(Shell存根):$caxa——输入和#34;示例/echo命令行参数"--输出";echo命令行参数。sh";——"{{caxa}}/node_modules/。bin/node""{{caxa}}/index。js""有些""嵌入参数""--这是命令的一部分#34;

下面是一个使用caxa的真实例子。这个例子包括Windows、macOS和Linux的打包;使用GitHub发布标签释放资产;通过GitHub Actions工件,为每一次推送分发内部人员构建;以及使用rsync将二进制文件部署到服务器(以及发布npm包,但这超出了caxa的范围)。

与其从命令行调用caxa,不如编写一个构建应用程序的程序,例如:

从"进口caxa;卡萨";(异步()=>;{wait caxa({input:";examples/echo命令行参数";,output:";echo命令行参数";{caxa}/node_modules/.bin/node#34;";{caxa 34}/index.js 34;,";";一些&##嵌入参数&#( ) ;

您可能需要检查流程。平台来确定您正在运行的操作系统,并提供适当的参数。

如果希望运行非node的命令,例如ts node,可以通过扩展路径来实现。例如,您可以在macOS/Linux上运行以下操作:

这只是macOS/Linux上的一个问题。在这些操作系统中,二进制文件必须启用可执行模式才能运行。您可以在类似于-rwxr-xr-x[…]的输出上使用ls-l:从命令行检查模式/bin/node,xs表示该文件是可执行的。

以下是分发二进制文件时可以执行的操作,以确保保留文件模式:

制作一个防水布或拉链。文件模式是通过压缩/解压来保持的,macOS/Linux(无论如何,大多数发行版)自带了解压tarball和zips的软件,用户只需双击文件即可。

有趣的事实:Windows 10还附带了tar可执行文件,所以上面的命令也适用于Windows。不幸的是,Windows上的文件资源管理器不支持解压缩文件。双击tgz(但它支持解压缩.zip)。幸运的是,Windows一开始就没有文件模式的问题(它只是查找.exe扩展名),因此直接分发caxa输出是合适的。

在某些情况下,这可能更有意义,但它需要用户使用命令行。

caxa不会对应用程序执行任何特殊操作,因此没有内置的方法来判断应用程序是否从打包版本运行。这是caxa尽可能远离尘嚣的精神气质的一部分。此外,我认为这是一个糟糕的做法:一个自我意识很强的应用程序更难推理和测试。

这就是说,如果你真的需要知道应用程序是否是从打包的版本运行的,下面是一些可能的解决方法,以提高不良程度:

接收一个嵌入到打包过程中的命令行参数,例如,";{{caxa}}/node_modules/。bin/node""应用js""--caxa";。

即使应用程序的代码位于临时目录中,调用打包的应用程序时的当前工作目录也会被保留,您可以使用进程来检查它。cwd()。这可能不是你必须考虑的caxa问题,只要把它做好就行了。

据我所知,为Node创建二进制文件是问题的根源。js项目是本机模块。本机模块是至少部分用C/C++编写的库,例如sharp、@leafac/sqlite(无耻的插件!),等等。本机模块至少有三个问题与此相关:

要安装这些库(C/C++编译器、make、Python等等),必须有一个运行正常的C/C++构建系统。在Windows上,必须安装Windows生成工具。在macOS上,必须使用xcode select--install安装命令行工具(CLT)。在Linux上,这取决于发行版,但在Ubuntu sudo上,apt install build essential就足够了。

本机模块的安装不是跨平台的。JavaScript依赖项可以从一个操作系统复制到另一个操作系统,与此不同,本机模块生成特定于安装依赖项的操作系统的编译C/C++代码。编译后的代码以的形式出现在node_modules目录中。节点文件。

据我所知,诺德。js坚持从磁盘中的文件加载本机模块。另一个节点。js打包解决方案可以通过以下两种方式之一绕过这个限制:要么修补节点。js欺骗它以不同的方式加载本机模块;或者他们把。在启动程序之前的某个位置创建节点文件。

caxa建立在推杆的理念之上。节点文件位于临时位置,但最终的结果是:caxa可执行文件是一种自解压存档形式,包含整个项目和节点可执行文件。当您第一次运行caxa生成的二进制文件时,它会将整个项目(以及捆绑的节点可执行文件)的源文件提取到一个临时位置。从那里,它只需调用打包项目时让它运行的任何命令。

一开始,这似乎成本太高,但实际上基本上没什么问题:首先解压缩项目不会花费太长时间,而且运行程序后caxa不会清理临时目录,因此后续调用会被有效缓存并在没有开销的情况下运行。

这个想法很简单,但它超级强大!caxa支持任何类型的项目,包括那些具有本机依赖项的项目,因为运行caxa可执行文件等同于安装节点。用户机器上的js。caxa生成包的速度很快,因为生成自解压归档文件只需连接一些文件。caxa支持任何版本的节点。js,因为它只是将调用它的节点可执行文件复制到自解压归档文件中。

有趣的事实:通过压缩归档文件,caxa生成的二进制文件与其他打包解决方案相比自然更小。显然,您可以通过压缩这些其他工具的输出来实现相同的结果,这些工具可能无论如何都想保留文件模式(请参见§保留二进制文件的可执行模式)。

你知道吗,你可以把任何东西附加到二进制文件中,它会继续工作吗?适用于Windows、macOS和Linux的二进制文件也是如此。下面是一个在macOS/Linux上试用的示例:

$cp$(这是ls)/ls#将“ls”二进制文件复制到当前目录中,以使用它$/ls#列出文件,证明二进制文件可以运行$echo ANYTHING>>;ls#在二进制$tail中添加材料/ls#你应该在输出$的末尾看到'ANYTHING'/ls#输出应与之前相同!$好了,考试结束了

caxa自解压档案由三部分组成:1。存根;2.档案;三,。页脚。这是caxa生产的二进制文件中这些部件的布局:

存根和存档由caxacaxa字符串分隔。档案和页脚之间用新行隔开。这种布局允许caxa只需从文件末尾向后看,直到到达换行符即可找到页脚。如果这是您第一次运行caxa可执行文件,并且归档文件需要解压缩,那么caxa可以通过从开始向前看,直到到达caxa分隔符,来找到归档文件的开始。

使用caxa构建一个二进制文件,并在文本编辑器中亲自检查(Visual Studio代码要求您确认是否要打开一个二进制文件,但在这之后可以正常工作)。您应该能够在存根和归档文件之间找到caxacaxa分隔符,以及末尾的页脚。

您可以在stubs/stub中找到存根的源代码。去您可以使用npm run build:stubs构建存根。您将需要一个Go编译器,但存根除了Go标准库之外没有任何依赖项,因此无需设置Go模块或配置$GOPATH。npm包中有针对主要平台的预编译存根。如果您希望验证存根是否真的是从存根/存根编译而来。go,您可以自己重新编译它,因为go编译器似乎是确定性的,并且总是在给定相同源的情况下生成相同的二进制文件(至少在我的测试中是这样)。

这在某种程度上很漂亮:我们使用Go的能力来生成二进制文件来引导节点。js生成二进制文件的能力。

这是一个JSON,包含caxa运行项目所需的额外信息:最重要的是,您要运行的命令,还有一个用于解压缩归档文件的标识符。

有趣的事实:没有节点。关于存根。您可以使用它们来解压缩任何类型的存档,并对输出运行任意命令!从零开始构建自解压归档相对简单。例如,您可以在macOS中运行以下操作:

$cp存根和ls caxa$tar-czf-README。md>>;ls caxa$printf";\n{";标识符\";:\";一个ls-caxa/an-arbitative-STRING-THAT-SHOULD-BE-DIFFERENT-TIME \";\";命令\";[\";ls\&";,\";{caxa 34;]" >>;一个ls caxa$/ls caxa自述文件。医学博士

这取决于操作系统。您可以通过以下方式在系统上找到位置:

为什么没有交叉编译?为什么没有不同版本的节点。除了调用caxa的版本之外,还有js吗?

我相信你应该有一个可以与你计划支持的所有操作系统协同工作的环境。它们可能不是您的主要开发环境,但它们应该能够构建您的项目并让您进行测试。至少,您应该使用像GitHub Actions这样的服务,它允许您在Windows、macOS和Linux上运行构建任务和测试。

(比如,我买了一台电脑在caxa上工作。这是支持我工作的另一个原因!)

最小惊喜原则。交叉编译时(例如,从macOS开发机器构建Windows可执行文件),或绑定不同版本的节点时。js(例如,在运行caxa和Node.js 14时绑定Node.js 15),没有直接的方法可以保证打包的项目将与未打包的版本运行相同。如果您没有使用任何本机模块,那么事情可能会成功,但一旦引入了一个您不知道是本机的新依赖项,您的应用程序可能会崩溃。本机依赖项不仅在操作系统上不同,而且在不同版本的节点之间也可能不同。js,如果这些版本不兼容ABI(这就是为什么有时在更新Node.js时必须再次运行npm安装)。

有趣的事实:如今,易于交叉编译的黄金标准是Go。但是,即使在Go中,只要引入C依赖项(称为CGO),交叉编译也会消失。Go社区中的许多人似乎都试图通过避免CGO依赖性来解决这个问题,有时他们会不遗余力地在纯Go中重新创造一切。一方面,这听起来很有趣。另一方面,这是一个巨大的非发明于此综合征病例。在任何情况下,本机模块似乎在Node中更为普遍。js而不是CGO正在运行,所以我认为在caxa中进行交叉编译是一件愚蠢的事情。

如果您仍然坚持交叉编译或编译不同版本的Node。js,您仍然可以使用存根手动构建自解压存档(请参阅§使用不带caxa的自解压存档)。你甚至可以使用https://www.npmjs.com/package/node以更轻松地捆绑不同版本的节点。js。

macOS应用程序包只是一个具有特定结构的文件夹,以及位于特定位置的可执行文件。创建macOS应用程序包时,caxa不会构建自解压归档,而是将应用程序复制到正确的位置,并创建一个可执行的bash脚本来启动该过程。

macOS应用程序包可以通过在Finder中双击来运行。它打开了一个终端。应用程序窗口中的应用程序。如果您运行的应用程序不是在您的机器上构建的(这很可能是您的用户的情况,他们可能从internet下载了该应用程序),那么第一次运行它时,macOS可能会抱怨缺少签名。解决方案是转到系统首选项>;保安及;隐私>;常规,然后单击允许。你必须指导你的用户如何做到这一点。

它相当于Go存根,只是它更小,因为它只有十几行Bash,但它取决于最终用户机器上安装的一些东西,例如tar、tail等等。

如果您对其中一个功能感兴趣,请尽可能发送请求,或者至少联系我并说明您的兴趣,我可能会找到它们。

其他压缩算法。目前,caxa使用tarball,它无处不在,在压缩/解压缩时间和归档大小方面相当有效。但是还有更好的算法…(参见https://github.com/leafac/caxa/issues/1.)

添加对可执行文件签名的支持。可签名的可执行文件的种类有限制,而caxa生成的那种自解压归档文件可能是不可签名的(我对此知之甚少……)。一个解决方案是使用Go的支持,将数据嵌入二进制文件中(该文件在Go 1.16中发布)。当然,这需要打包项目的人有一个运行的Go-build系统。另一个解决方案是将可执行文件作为数据结构进行操作,而不是仅仅在末尾添加内容。Go在标准库中有实现这一点的工具,但是打包程序本身(不仅是存根)必须用Go编写,并且在命令行上通过简单地连接文件来创建包是不可能的。

添加对自定义图标和其他包元数据的支持。通过使用rcedit for,这应该相对简单。加上。plist文件到。应用程序(我们也可以复制Electron在这里做的任何事情)。

下面是我在构建caxa的过程中学到的所有知识的扩展版本。

Deno提供了生成二进制文件的实验支持。我自己也没试过,但也许有一天它流行起来,caxa就过时了。希望如此!

pkg很棒,这是我第一次了解到你可以

......