开源,NMaker正式发布,自动生产Makefile,自动工程管理

老邓 2009-10-06 12:15:46
最近使用Eclipse CDT,越用越喜爱。可是,由于使用VC编译器,Eclipse CDT只能自己写Makefile,书写依赖关系,添加编译文件...这是一件非常烦琐的工作。NMaker将解决这个问题。
通过NMaker,可以自动产生头文件依赖关系、可以自动管理工程,一切都是自动化的完成。你需要做的可能只是一次鼠标双击、可能只是修改一个配置值,就可以编译、调试、运行,就可以在ANSI/Unicode、Debug/Release之间自由切换。通过设置命令行编译环境,可以方便的从32位平台、64位平台切换编译。
如果你不使用Eclipse CDT,而是使用除VS、Code::Blocks以外的其他编译器编辑代码,也可以很方便的使用NMaker来管理工程。
欢迎使用!

编译好的NMaker和源码,请到此页面下载,因为想统计下载次数(没广告)...

核心代码开源,非经许可,不得用于商业用途!

核心代码
Main.cpp
int _tmain(int argc, TCHAR* argv[])
{
// 处理运行参数
OptData od;
od.onlyMak = false;
GetOptToData(argc, argv, od);
if ((od.onlyMak && (od.baseDir.empty() || od.projType.empty())) || (!od.onlyMak && od.baseDir.empty()))
{
PrintHelpInfo();
return -2;
}

// 创建Makefile
if (od.onlyMak)
{
CreateMakefile(od.baseDir, od.projType);
return 0;
}

// 获得源文件与头文件对应的依赖文件,即基本依赖
DependBase depBase;
depBase.psr.SetBasePath(od.baseDir);
depBase.psr.SetIncludePath(od.incDir);
depBase.pch = qp::Path::Append(od.baseDir, od.pchSrc);
qp::Search s;
s.RegisterNotify(&SearchCorCppFiles);
s.Run(od.baseDir, &depBase);

// 递归得到源文件对应的所有依赖,即完整依赖
Depend dep(depBase.header, depBase.src);
dep.Parser();

// 输出到文件
qp::File file;
qp::String depFile(qp::Path::Append(od.baseDir, _T("Depends.mk")));
if (!file.Create(depFile, GENERIC_WRITE, FILE_SHARE_WRITE, CREATE_ALWAYS))
return -3;

#ifdef _UNICODE
WORD bom = 0xFEFF; // 添加Unicode BOM
file.Write(&bom, sizeof(WORD));
#endif

// 添加版权声明
qp::String toFile;
toFile.reserve(1000 * 1000);
toFile += _T("#----------------------------------------------\r\n")
_T("# Copyright (C) QPSOFT.COM All rights reserved.\r\n")
_T("#----------------------------------------------\r\n")
_T("\r\nOBJS = \\\r\n");

// 添加OBJ列表,将OBJ文件保存在Vector中,为后面写编译规则时提速
VecString obj;
for (auto it = depBase.src.begin(); it != depBase.src.end(); ++it)
{
const qp::String& src = (*it).first;
qp::String tmp(_T("\"$(OUT)"));
tmp += src.substr(1, src.rfind(_T('.')));
tmp += _T("obj\"");
toFile += _T("\t") + tmp + _T(" \\\r\n");
qp::String cc(_T("SRC=" + src + _T("\r\n")) + tmp + _T(" : $(SRC) \"$(OUT)\""));
if (!od.pchSrc.empty()) cc += _T(" \"$(PCH)\"");
cc += _T("\r\n");

const qp::String::size_type pos = src.rfind(_T('\\'));
if (pos > 2)
{
cc += _T("\t$(CC) $(CFLAGS) /Fo$(OUT)");
cc += src.substr(1, pos) + _T(" $(SRC)\r\n");
}
cc += _T("\r\n");
obj.push_back(cc);
}

// 添加编译规则
toFile += _T("\r\n");
for (auto it = obj.begin(); it != obj.end(); ++it)
{
toFile += *it;
}

// 添加依赖关系
for (auto it = depBase.src.begin(); it != depBase.src.end(); ++it)
{
toFile += (*it).first + _T(" : \\\r\n");
for (auto i = ((*it).second).begin(); i < ((*it).second).end(); ++i)
{
toFile += _T("\t\"") + *i + _T("\" \\\r\n");
}
toFile += _T("\r\n");
}

// 写入文件
file.Write(toFile.c_str(), toFile.size() * sizeof(TCHAR));

qp::Cmd::Printf(_T("NMaker: Created '%s' !\n"), depFile.c_str());
return 0;
}

Parser.cpp

Parser::Parser(const qp::String& basePath, const VecString& searchPath) :
m_basePath(basePath), m_incPath(searchPath)
{}

void Parser::ParserFile(const qp::String& file)
{
m_fileName = qp::Path::RelativePathTo(m_basePath, file);
m_filePath = file;
qp::Path::RemoveFileSpec(m_filePath);

m_depFile.clear();

std::vector<TCHAR> buf;
if (ReadFileToBuffer(file, buf))
ParserBuffer(buf);
}

bool Parser::ReadFileToBuffer(const qp::String& filePath, std::vector<TCHAR>& buf)
{
qp::File file;
if (!file.Create(filePath, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING))
return false;

DWORD size = static_cast<DWORD> (file.GetSize());

// 最大只处理4K,为了效率
if (size > 4096)
size = 4096;
buf.resize(size);
file.Read(&buf[0], size);
return true;
}

void Parser::ParserBuffer(std::vector<TCHAR>& buf)
{
buf[buf.size() - 1] = '\0';
std::vector<TCHAR>::size_type max = buf.size() - 8;
for (std::vector<TCHAR>::size_type i = 0; i < max; ++i)
{
TCHAR* p = &buf[i];
if (*p == '#' && *++p == 'i' && *++p == 'n' && *++p == 'c' && *++p == 'l' && *++p == 'u' && *++p
== 'd' && *++p == 'e')
{
i += 7;

// 不是第一行代码,则#include之前必须是换行符
if (i > 8 && buf[i - 8] != '\n')
continue;

// 去除#include与"或<之间的空格、Tab
do ++p;
while (*p == ' ' || *p == '\t');

// 获取包含文件的长度
int len = 0;
if (*p == '"' || *p == '<')
{
++p;
while (*(p + len) != '"' && *(p + len) != '>')
{
if (++len > 100) break;
}
}

// 判断文件是否存在,如果不存在,则到搜索目录中查找,只匹配第一个符合条件的文件
qp::String fileName(p, len);
qp::String filePath = qp::Path::Append(m_filePath, fileName);
if (!qp::Path::FileExists(filePath))
{
bool finded = false;
for (auto it = m_incPath.begin(); it < m_incPath.end(); ++it)
{
filePath = qp::Path::Append(*it, fileName);
if (qp::Path::FileExists(filePath))
{
finded = true;
break;
}
}

if (!finded) filePath.clear();
}

// 判断相对路径
if (!filePath.empty())
{
fileName = qp::Path::RelativePathTo(m_basePath, filePath);
if (!fileName.empty())
{
qp::StringEx::Replace(fileName, '/', '\\');
m_depFile.push_back(fileName);
}
}
}
}
}

Depend.cpp

Depend::Depend(DependMap& header, DependMap& src) : m_depHeader(header), m_depSrc(src)
{}

void Depend::GetDependFromHeader(VecString& srcDep, qp::String& header)
{
auto iter = m_depHeader.find(header);
if (iter != m_depHeader.end())
{
AddDependToSrcVector(srcDep, (*iter).second);
m_fileIsParser.push_back((*iter).first);

for (auto it = ((*iter).second).begin(); it < ((*iter).second).end(); ++it)
{
bool isParser = false;
for (auto i = m_fileIsParser.begin(); i < m_fileIsParser.end(); ++i)
{
if (*i == *it)
{
isParser = true;
break;
}
}

if (!isParser)
GetDependFromHeader(srcDep, *it);
}
}
}

void Depend::AddDependToSrcVector(VecString& srcDep, VecString& headerDep)
{
for (VecString::size_type i = 0; i < headerDep.size(); ++i)
{
bool isExist = false;
for (VecString::size_type j = 0; j < srcDep.size(); ++j)
{
if (headerDep[i] == srcDep[j])
{
isExist = true;
break;
}
}

if (!isExist)
srcDep.push_back(headerDep[i]);
}
}

void Depend::Parser()
{
for (auto it = m_depSrc.begin(); it != m_depSrc.end(); ++it)
{
m_fileIsParser.clear();

VecString& srcDep = (*it).second;
VecString::size_type max = srcDep.size();
for (VecString::size_type i = 0; i < max; ++i)
{
GetDependFromHeader(srcDep, srcDep[i]);
}
}
}

...全文
1185 58 打赏 收藏 转发到动态 举报
写回复
用AI写文章
58 条回复
切换为时间正序
请发表友善的回复…
发表回复
aslprince 2010-08-13
  • 打赏
  • 举报
回复
感谢lZ分享。。。坚决顶LZ~~~~·

实用软件的源码~~~
frost_whisper 2009-12-04
  • 打赏
  • 举报
回复
膜拜……
码农天天向上 2009-11-19
  • 打赏
  • 举报
回复
老邓~~~
顶~~~
老邓 2009-11-19
  • 打赏
  • 举报
回复
[Quote=引用 54 楼 chenxs2003012233 的回复:]
问一下,这是在windows下用
还是在linux下用?
[/Quote]
Windows下使用。
用于nmake
chenxs2003012233 2009-11-18
  • 打赏
  • 举报
回复
问一下,这是在windows下用
还是在linux下用?
老邓 2009-10-08
  • 打赏
  • 举报
回复
[Quote=引用 52 楼 jackyjkchen 的回复:]
我在说明里看到了传说中的Z盘,老邓该不会和我一样盘符排到Z了吧
[/Quote]
^_^ 不是,我是将U盘的盘符设置为Z,这样我的工程都放在U盘里,我走到哪里,都可以编译我的工程。
就是图一个省事。

分享一个批处理,用于将指定盘符改为其他盘符的:
set old=G:
set new=Z:
for /f %%i in ('mountvol %old% /l') do set "vol=%%i"
mountvol %old% /d
mountvol %new% %vol%
popd
jackyjkchen 2009-10-08
  • 打赏
  • 举报
回复
我在说明里看到了传说中的Z盘,老邓该不会和我一样盘符排到Z了吧
qqwx_1986 2009-10-08
  • 打赏
  • 举报
回复
支持
老邓 2009-10-08
  • 打赏
  • 举报
回复
[Quote=引用 49 楼 jackyjkchen 的回复:]
我是第一个下的
[/Quote]
^_^
这几天的业余时间都花在上面了。
今天写了几个例子,都使用NMaker来生成依赖、管理工程。
很爽!
jackyjkchen 2009-10-08
  • 打赏
  • 举报
回复
我是第一个下的
borefo 2009-10-08
  • 打赏
  • 举报
回复
谢谢分享
老邓 2009-10-08
  • 打赏
  • 举报
回复
正式发布,源码打包提供下载。
供参考,有问题请反馈。
xingzhe2001 2009-10-08
  • 打赏
  • 举报
回复
支持楼主!!
pcboyxhy 2009-10-08
  • 打赏
  • 举报
回复
[Quote=引用 43 楼 jackyjkchen 的回复:]
引用 37 楼 pcboyxhy 的回复:
引用 23 楼 jackyjkchen 的回复:
引用 12 楼 milkylove 的回复:
代码的对齐风格跟编辑器没关系,只跟程序员的水平有关。只要素质稍好一点的程序员 ,都会注意代码的对齐。

手工控制怎么编辑都可以,不过我倒是用过gvim的自动格式化,出来的就是Vc里STL源码的效果,VS+VA的自动格式化,就是楼主的效果


建议把vim配置好后再使用,不用=格式化,一路换行写下来的代码,风格也不会差
不喜欢GNU风格的,可以不做自动格式化,或者自己改写一下C和C++格式化模版

我觉得自动格式化不是好事,写代码的时候不注意层次缩进,搞的一团糟,把希望寄托在自动化工具上
会降低对代码结构的理解,也容易让人丧失自己的风格,跟自动补全一样,这类功能更加适合新手

Python中的“{”和“}”,是通过缩进完成的,代码的层次依靠缩进,不依靠{}
缩进的不好,在python中就是错误,把缩进作为语法来要求的语言,阅读起来非常清晰

用Windows的人往往打字速度不超过100字/分(除了录入、秘书和经常聊天的),所以还是少打点字感觉好,我不感到我打一个{
自动出现
{
    //光标位置
}
会对我的思路造成什么不良影响,相反要舒服得多,而且Windows里的api编程如果没有自动完成的话,效率会低到无法想象,想我经常用到的QueryPerformanceCounter之类的函数,在WindowsAPI里也不算是最长的,自己打……会晕死的,而且还要经常忘记拼写,遇到没有VS+VA的机器,我会用搜狗输入法挂一个英文词库+Windows API词库,用中文输入法来输入API,也有自动完成的效果。
[/Quote]


离开它们就降低效率,恰好说明产生依赖性了

API的命名比较规则,很好记忆
常用的API就那么几个,打出来花不了几秒钟

喜欢自动插入{//todo},可能是深受MFC熏陶吧
jackyjkchen 2009-10-08
  • 打赏
  • 举报
回复
晕,是不会造成不良影响……
jackyjkchen 2009-10-08
  • 打赏
  • 举报
回复
[Quote=引用 37 楼 pcboyxhy 的回复:]
引用 23 楼 jackyjkchen 的回复:
引用 12 楼 milkylove 的回复:
代码的对齐风格跟编辑器没关系,只跟程序员的水平有关。只要素质稍好一点的程序员 ,都会注意代码的对齐。

手工控制怎么编辑都可以,不过我倒是用过gvim的自动格式化,出来的就是Vc里STL源码的效果,VS+VA的自动格式化,就是楼主的效果


建议把vim配置好后再使用,不用=格式化,一路换行写下来的代码,风格也不会差
不喜欢GNU风格的,可以不做自动格式化,或者自己改写一下C和C++格式化模版

我觉得自动格式化不是好事,写代码的时候不注意层次缩进,搞的一团糟,把希望寄托在自动化工具上
会降低对代码结构的理解,也容易让人丧失自己的风格,跟自动补全一样,这类功能更加适合新手

Python中的“{”和“}”,是通过缩进完成的,代码的层次依靠缩进,不依靠{}
缩进的不好,在python中就是错误,把缩进作为语法来要求的语言,阅读起来非常清晰
[/Quote]
用Windows的人往往打字速度不超过100字/分(除了录入、秘书和经常聊天的),所以还是少打点字感觉好,我不感到我打一个{
自动出现
{
//光标位置
}
会对我的思路造成什么不良影响,相反要舒服得多,而且Windows里的api编程如果没有自动完成的话,效率会低到无法想象,想我经常用到的QueryPerformanceCounter之类的函数,在WindowsAPI里也不算是最长的,自己打……会晕死的,而且还要经常忘记拼写,遇到没有VS+VA的机器,我会用搜狗输入法挂一个英文词库+Windows API词库,用中文输入法来输入API,也有自动完成的效果。
dollfacedboyfriend 2009-10-08
  • 打赏
  • 举报
回复
为什么不用纯c和clib?这样兼容性会比较好!
xylicon 2009-10-07
  • 打赏
  • 举报
回复
支持开源
老邓 2009-10-07
  • 打赏
  • 举报
回复
[Quote=引用 36 楼 lights_joy 的回复:]
if (*p == '#' && *++p == 'i' && *++p == 'n' && *++p == 'c' && *++p == 'l' && *++p == 'u' && *++p
            == 'd' && *++p == 'e')

这样的判断似乎简单了点,是否考虑用一个简单的词法分析器代替?
[/Quote]
嗯,是啊。
目前主要是想完成功能,要假设用户的头文件这样包含:
#include "someheader.h"
#include "otherh.h"
...
老邓 2009-10-07
  • 打赏
  • 举报
回复
[Quote=引用 34 楼 jackyjkchen 的回复:]
引用 33 楼 loaden 的回复:
引用 31 楼 akirya 的回复:
到那个sf上开一个项目吧。

嗯,工具太小,没啥意思。
明天晚上放出可执行文件,本来想今天晚上放出Demo,但启动参数部分没弄好。

怎么没意思了,多小的工具好用就好,放出来我试用
[/Quote]
这个是给VC的nmake用的。
主要用在Eclipse CDT上。
只适合Windows平台。
加载更多回复(38)

64,665

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧