php⾃动⽣成C代码并⾃动编译运⾏
背景
有时我们需要⽤C语⾔编写⼀些⼯具程序,这些⼯具程序有些内容可以共享,但不⽅便⽤函数进⾏复⽤。这时可以考虑⽤代码⾃动⽣成技术。本⽂⽤⼀个例⼦介绍如何通过模板⾃动⽣成c语⾔程序,并调⽤编译器⽣成可执⾏程序,最后执⾏可执⾏程序的完整过程。
实现过程
1,⽤于⽣成c代码的数据源,json格式的⽂件 blockStruct.json
[{"student":{"size":260,"elements":{"name":{"type":"char","len":100,"offset":0},"num":{"type":"int","len":0,"offset":100},"age":{"type":"int","len":0,"offset":104}," addr":{"type":"char","len":150,"offset":108}}}},{"teacher":{"size":356,"elements":{"name":{"type":"char","len":100,"offset":0},"num":{"type":"int","len":0,"offset": 100},"addr":{"type":"char","len":250,"offset":104}}}}]
2,⽤于⽣成c代码的模板⽂件:fetchStructInfo.c
//⾃动⽣成的c代码,⽤于获取结构体⼤⼩,元素偏移量等信息
#include<string.h>
#include<stdio.h>
#include"<?=$structSrc?>"
FILE* fout;
<?php foreach ($structArray as $key=>$value){?>
//输出<?=$key?>结构体信息,此处的type的代码:6,1等,要与做数据转换的程序配合⼀致才⾏。
phpjson格式化输出void output<?=$key?>Info(){
struct<?=$key?> o;
fprintf(fout,"{");
<?php $i=0;foreach ($value as $k=>$v){ $tag = $i++?',':'';?>
fprintf(fout,"<?=$tag?>\"<?=$v[0]?>\":{\"type\":\"<?=$v[1]?>\",\"len\":<?=$v[2]?>,\"offset\":%d}",(char*)(&(o.<?=$v[0]?>))-(char*)(&o)); <?php }?>
fprintf(fout,"}");
}
<?php }?>
//将结构体块的⼤⼩等信息⽤json的形式输出到⽂件
int outputBlockInfo(char* fileName){
if((fout =fopen(fileName,"w"))==NULL)
{
printf("file->%s can not open!\n",fileName);
return1;
}
//备注:json串的键名加上双引号可保证⼤多数的json解析器都能认识
fprintf(fout,"[");
<?php $i=0;foreach ($structArray as $key=>$value){ $tag = $i++?',':'';?>
fprintf(fout,"<?=$tag?>{\"<?=$key?>\":{\"size\":%d,\"elements\":",sizeof(struct<?=$key?>));
output<?=$key?>Info();
fprintf(fout,"}}");
<?php }?>
fprintf(fout,"]");
fclose(fout);
return0;
}
int main(int argc,char* argv[])
{
//结构体信息⽂件名
char* infoFileName ="<?=$outputFileName?>";
//输出结构体的⼤⼩,元素名称,类型等信息
outputBlockInfo(infoFileName);
return0;
}
3,⽤于⽣成c代码的php脚本⽂件tempCodeWorker.php
<?php
namespace Ados;
require_once'const.php';
require_once STRUCT_PARSER.'structwkr_engine.php';
$engine=new StructwkrEngine();
//读取c语⾔结构体定义⽂件的内容
$src=file_get_contents('structSrc/blockStruct.h');
//调⽤引擎的编译功能,获取源⽂件中定义的结构体的信息
$engine->compile($src);
//获取源语⾔中定义的结构体信息,包括元素名称,类型,查不包括结构体的实际⼤⼩,元素的偏移量
//这类信息通过动态⽣成⼀个c代码,执⾏后取得相应的信息。
$structArray=$engine->structInfo();
//⽤于替换的模板变量
$structSrc="../structSrc/blockStruct.h";
$outputFileName="../structSrc/blockStruct.json";
//替换模板⽂件,输出内容就是要⽣成的c代码
require_once"template/fetchStructInfo.c";
>
4.⽤于⽣成编译c代码的批处理⽂件的模板⽂件:build.inc
rem 编译⽣成
set PATH=%PATH%;<?=$vcPath?>\bin
set PATH=%PATH%;<?=$vcIDEPath?>
< temp.c /I "<?=$vcPath?>\include" /link "<?=$vcPath?>\lib\libcmt.lib" "<?=$vcPath?>\lib\oldnames.lib" "<?=$sdkLibPath?>\Kernel32.Lib"
5, ⽤于⽣成批处理⽂件的php脚本⽂件tempBatWorker.php
<?php
//⽤于替换的模板变量
$vcPath='D:\Program Files (x86)\Microsoft Visual Studio 10.0\VC';
$vcIDEPath='D:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE';
$sdkLibPath='C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Lib';
//替换模板⽂件,输出内容就是要⽣成的批处理⽂件
require_once"template/build.inc";
>
6, 运⽤重定向技术,将模板替换后的输出内容重定向⽣成最终所需的C代码,以下php⽂件命名为fetchStructInfo.php
<?php
//调⽤tempCodeWorker,将输出重定向到⽂件,得到所需的c源代码
exec('php tempCodeWorker.php > tempfile/temp.c');
//调⽤tempBatWorker,将输出重定向到⽂件,得到所需的批处理脚本
exec('php tempBatWorker.php > tempfile/build.bat');
>
执⾏第6步所定义的php脚本,将会得到⼀个c语⾔源代码⽂件和⼀个⽤于编译此⽂件的脚本⽂件。因此我们最后需要⼀个主脚本⽂件,将这些步骤串起来,⽣成C代码,⽣成编译所需的批处理⽂件,执⾏编译脚本得到可执⾏⽂件,运⾏可执⾏⽂件。这个主脚本⽂件fetchStructInfo.bat内容如下:
rem 调⽤fetchStructInfo.php ⽣成临时c代码与⽤于编译并执⾏的批处理⽂件
php fetchStructInfo.php
rem 执⾏此批处理命令,编译得到可执⾏⽂件并执⾏之,得到结构体信息⽂件
cd tempfile
build.bat
pause
关于调⽤cl的补充说明
由于本⽂中动态⽣成的c代码很简单,因此想⽤命令⾏的⽅式进⾏编译。
如果直接就⽤ cl.exe temp.c
多半会报 cl.exe不是可执⾏的命令。所以要设置环境变量。
这⾥有两个办法,
第⼀种:
执⾏:“D:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\bin\vcvars32.bat”
这个批处理脚本会将vc编译所需的环境配置好。并且这个vcvars32.bat复制到其它⽬录后再执⾏效果也⼀样。不过这个⽅法有⼀个缺点,就是设置的内容较多,需要较长的时间,好像要1秒多。
第⼆种:
如果只是做⼀个简单的编译,所以还是直接设置环境变量⽐较好⼀点,⽽且是不在整个系统范围内设置,只在单个会话中设置,对其它会话也没有影响。
所以先执⾏
set PATH=%PATH%;D:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\bin
但是接下来还是会报⼀些连接错误,所以还需要指定相应的包含路径和连接库
在本⼈所在电脑进⾏编译的完整的命令应该如下:
set PATH=%PATH%;D:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\bin
set PATH=%PATH%;D:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE
< temp.c /I “D:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\include” /link “D:\Program Files
(x86)\Microsoft Visual Studio 10.0\VC\lib\libcmt.lib” “D:\Program Files (x86)\Microsoft Visual Studio
10.0\VC\lib\oldnames.lib” “C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Lib\Kernel32.Lib”
这样整个执⾏下来会很快。
有了以上的知识做基础,就可以采⽤模板替换的⽅式⽣成可以在其它机器上运⾏的批处理脚本。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论