在NetBeans中一键编译
撰写时间:2024-11-12
修订时间:2024-11-13
使用Emscripten编译的问题
使用Emscripten的过程,实质上就是一个编译并运行C语言程序的过程,并且,还要加上与Web端相整合的步骤。因此,不像想像中那么轻松。
其过程主要涉及这几个环节:
- 在系统中设置PATH及相应变量名称
- 在IDE中编写并保存.c源代码
- 在命令行编译为相关文件
- 部署到服务器
- 打开浏览器并访问相关的网页
- 在学习、研究过程中需要分类保存不同的代码
有没有比较简便的操作,比如,修改好代码后,一键就可实现自动设置环境变量、保存、编译、部署、显示最终的HTML页面?
有。
首先,我们需要解决架设服务器的问题。我使用NetBeans,并已经安装好了PHP的环境。这样,在NetBeans中新建一个PHP项目,则就会自动在本地上部署并运行项目。因此,我将结合PHP项目的特性来讲解如何实现目标。
需要部署的路径结构
先看我们最终需要达成目标的文件路径结构。
- webassembly
- examples
- playgrounds
- deploy
- wasm-playground.html
- script
- build.sh
- src
- hello.c
- deploy
- hello-world
- [... any other demo folders]
- playgrounds
- examples
我们相关的代码准备集中放在webassembly/examples下面。如上图所见,该路径下已有playgrounds, hello-world以及其他的项目文件夹,分别存放不同子项目的源代码。现在,我们准备对playgrounds子项目进行编译及部署。
该项目文件夹下有3个子文件夹。其中src用以存放C语言源代码,script用以存放帮我们自动编译的脚本,deploy存放最终在Web上呈现时所需要用到的各个文件。
根据上面各节的内容可知,Emscripten所自动生成的.html并不总是符合我们的要求,因此我们在了解其接口结构后可以根据需求定制所需版本。wasm-playground.html的内容很简单,其body的代码如下:
只是简单地设置了print及printErr的两个回调函数的内容。
hello.c的内容:
因为我们已经有了自己的.html,因此我们只需编译生成.js及.wasm这2个文件并放置进deploy目录就行了。下面是build.sh脚本的主要代码:
要编译成功,前提是我们需事先设置好了emcc的环境路径,并且当前工作路径在playgrounds路径下面。这些细节下面将谈到。
NetBeans的一键运行功能
NetBeans为不同类型的项目,均提供了一键运行的功能,特别方便,这里有初步的介绍。但对于PHP项目来讲,则需要一些额外的步骤。
我们先来实现最简单的一键运行效果。
- 先在PHP项目的一个对外界来讲是安全的路径下,生成一个emcc-dev.php文件。内容先为空。
何为对外界安全的路径?一个Web项目总有一个根目录,这个根目录就是存放访问者访问您的网站时所看到的首页的目录。如本站的Apache服务器管理必读所述,诸如
www
或public_html
等命名的文件夹都是比较常见的Web根目录。重要的私密文件,不能放在这些目录下面。而如果不是Web根目录的目录,则是安全的,外界不可以访问,但PHP项目管理器可以进行安全的内部访问。
- 在NetBeans的工具栏上点击
Set Project Configuration
下拉箭头,在弹出的菜单中点击
Customize...
- 弹出
Project Properties...
窗口:点击
New ...
按钮。 - 弹出
Create New Configuration
窗口:在文本框中填写
emcc-dev
,单击OK
按钮。 - 返回至
Project Properties
窗口:Configuration
一栏中已自动选择了刚刚生成的emcc-dev
,下面是其各项设置。在
Run As:
一栏中,选Script (run in command line)
。其意思是,在命令行运行特定的PHP文件。PHP Interpreter
是NetBeans根据系统设置帮我们自动填写的,可不管它。Index File
一栏,选择我们在第一步中所创建的emcc-dev.php文件。其他栏目留空。Arguments:
一栏,是可以填写要传给emcc-dev.php文件的参数的。但因为我们希望实现按下同一按钮而运行不同子项目的效果,每个子项目若都在这里设置,反倒较累,后面有更好的解决方案,因此这里可以不填。单击
OK
按钮,关闭该窗口。设置完毕。 - NetBeans的工具栏上显示了刚创建的运行项:
点击上面使用红框标出的
运行
按钮,NetBeans将自动打开一个Output
窗口,并显示:"/Applications/MAMP/bin/php/php8.2.0/bin/php" "< YOUR_PHP_PROJECT_DIR >/others/etc/shell-scripts/emcc-dev.php" Done. 发生了什么?PHP引擎运行了我们上面所创建的emcc-dev.php文件。当然,该文件内容目录为空,因此,什么都不会发生。
- 修改emcc-dev.php文件内容为:
<?php system("Open -u http://localhost"); PHP的代码通过调用system函数,执行了系统的Shell脚本命令,在浏览器中打开了连向
http://localhost
的网页。
上面这些步骤,我们实现了在NetBeans中只需一键即可在浏览器立即访问特定网页的功能。但这是最后的结果,在此之前,我们还需要实现两步:一是自动设置Emscripten的环境变量,二是调用emcc命令来编译。
设置Emscripten的环境变量
因为设置Emscripten的环境变量是通用的,因此,我们在上面emcc-dev.php文件所在目录创建一个名为export-emcc.sh的文件,内容如下:
根据您系统的设置予以相应的修改。
这是在Unix, Linux系统下设置环境变量及路径的命令。有此设置,将让我们在不同的目录下方便地调用emcc进行编译。
修改emcc-dev.php的内容如下:
上面代码,我们的意图是,设置环境变量,调用emcc命令以查看其版本号,然后再在浏览器中访问网页。
一键运行。但结果是Output窗口出现了红色警示:
意为,找不到emcc命令。这是因为环境变量的设置,只在PHP的system函数所孵化的线程内有效。将上面内容修改为:
将上面的3个命令,先放在一个数组中,然后再组合为一个字符串,最后让system函数整体执行。
一键运行,环境变量设置成功,打印了版本信息,浏览器正常访问网页。成功。但emcc的版本信息是红色的,这是其本性,没有错误,不用管它。
编译代码
终于来到最后一步了。现在,既然环境变量已设置好,我们希望调用上面所提到的build.sh脚本文件,让其帮助我们编译。再次修改emcc-dev.php内容如下:
为方便说明,这里使用了绝对路径的方式,将上面YOUR_PATH_SETTINGS
改为您自己的路径。
一键运行。出现红色警示:
这回的红色真是错误信息了,意为,emcc找不到src/hello.c这个文件。
在build.sh文件的最上面加入一行以打印当前工作路径:
我们发现,当前工作路径为emcc-dev.php的路径,而不是build.sh所在的路径。因此,上面使用相对路径来查找hello.c,难怪找不到。
修改build.sh文件内容如下:
其实系统自然是了解build.sh的绝对路径的,上面的第一行代码,我们向系统求助:我正在访问这个文件,请将这个文件的绝路径告诉我。
得到路径后,使用cd命令,将当前工作路径转入其下面,再转到其父目录,这样,emcc就可将src/hello.c编译进deploy目录下面了。
一键运行,红色的错误警示信息消失。
真正就差最后一步了:浏览器中访问的网页还不是最终的网页。
上面,主要涉及两个文件的路径。一是脚本的路径,使用绝对路径的方式。另一种是网页的路径,使用URL的方式。在同一个PHP项目内,它们有其相应的关系。为使这个一键运行更有通用性,我们再次修改emcc-dev.php的内容如下:
让PHP帮我们自动组装成相应的脚本路径及URL网址。将$doc_root改为您自己的路径。
上面各个变量,遵循我们自己所定下的默认约定,一般不予更改。例如,将各个子项目统一放在$doc_base_url下面;使用$script_path存放脚本文件,且脚本名称默认为build.sh;部署路径默认为deploy
。
而对于各个子项目,只需修改两个变量。$doc_branch为各个子项目名称,$webpage_name为打开浏览器时自动访问的网页名称。有了这样的约定,以后如果增加了子项目,仅修改这两个地方就可以了,还是很方便的。
效果检验
好了,一切准备妥当。看一下效果。将关注焦点放在hello.c上面。在NetBeans中修改其源代码。
上面代码的效果,是对变量num的值分别加上了2个30。将其值修改为20,然后,一键运行。不到1秒,网页就自动显示出来了:查看结果。神速!
改进:第2版
上面最大的问题在于,将doc_branch,也即子项目的名称,藏进了emcc-dev.php中,因此如果需要切换至不同的子项目,必须修改后者的内容。并且,与子项目相关的变量,也均写进了其中,模糊、扭曲了其作为公共调用接口的性质。
现在,我们制作第2版。在此版本中,我们通过界面来选择子项目,并且将子项目可能需要改变的地方集中放置于一个地方。
最终部署后的示意图如下:
- webassembly
- examples
- using-template
- deploy
- demo.html
- demo.js
- demo.wasm
- html-template
- template.html
- script
- build.php
- build.sh
- src
- hello.c
- deploy
- [... any other demo folders]
- using-template
- examples
我们准备使用模板来生成上面的结构。script下面的build.php是NetBeans中得以一键运行的所需文件,build.sh是使用zsh来实际编译的脚本。
在NetBeans中新建一个名为emcc-compile
的Project Configuation,如下图所示:
在Index File
一栏,我们通过界面上的Browse...
按钮,很方便地选择了using-template
子项目下面的build.php文件。以后要切换不同的子项目,只需在该栏重新选择相应子项目路径下的srcript/build.php即可。无需再改脚本内容。
Arguments
一栏,我们以相对路径指定了def-vars.sh的文件,该文件将以参数的形式,由build.php向build.sh传递。这样,不同的子项目,就可以自动获取到其路径并予以调用。该设置对所有子项目通用,无需更改。
依上面配置完后,一键运行时,NetBeans将以命令行的方式来调用build.php,该文件内容为:
$curr_php是build.php的绝对路径,$def_file使用$argv[1]
用于接收上面界面中的Arguments
所指定的others/etc/shell-scripts/def-vars.sh
字符串,这是def-vars.sh的相对路径,使用Perl流派的正则表达式将其转换为PHP项目路径及其def-vars.sh的绝对路径。
在此例子中,PHP项目的一级子目录结构如下:
- SarkuyaCom
- others
- wwwroot
代码:
用于加载并运行build.sh脚本文件,并将project_dir及def_full作为参数传给该脚本。类似于:
Apple在其Including One Shell Script Inside Another (Sourcing)一文中讲到,使用.
有较好的兼容性。
build.sh文件内容:
为使用代码更清晰,这里使用了脚本函数。
上面代码在接收了project_dir及def_full参数后,在set_vars函数中,以变量$project_dir作为参数运行变量名为$def_full脚本文件,也即def-vars.sh文件。该文件内容如下:
上面代码为后面的脚本设置了emcc的环境变量及代码变量doc_root及local_url_root。在从def-vars.sh返回后,build.sh则可方便地使用这些变量。
回到build.sh脚本文件,它即可使用上面的变量,分别在complie及open_url函数中予以调用以完成特定的工作。
在config函数中,我们可在此集中设置子项目相关的属性,如C源代码的文件名称,HTML模板名称,以及最终输出的网页名称等等。将此函数作列在最顶端,可方便我们快速而清晰地为不同子项目设置相应的属性。
完毕。
一键运行,NetBeans的Output窗口显示:
现在,只需一个傻瓜式的emcc-compile
按钮,足以应对众多不同的子项目。
本节小结
多年以前,我使用DocBook写作,也是利用了类似原理的技术,只是通过Ant来实现,所生成的HTML及PDF既快又美观。以后将以DocBook专题予以分享。
因此,充分挖掘我们手边工具的潜力,可让生产效率成倍以上提高。虽然在攻关时总会花费一定的时间,但一旦成功,则我们以后将受益匪浅。