• 在Windows下使用nmake+Makefile+编译ZThread库(附例子)


    ----------2015/01/09/23:21更新-----------------------------------

    关于保留DEBUG信息的一个简单例子,见这篇随笔

    ----------2014/12/18/17:53更新-----------------------------------

    前段时间写了这篇随笔,现在回过头来看感觉有很多问题,因此打算修正和补充一下,以前写的内容也没删,就留在这篇随笔的最下面了,可以对比着看看

    目的:编写使用ZThread库的多线程程序

    Windows: (Win7)

    因为ZThread是支持Windows平台的,所以windows下,最好用visual studio的编译器,确保最好的兼容性

    安装visual studio之后,通过开始>所有程序>Visual Studio 2013>Visual Studio Tools>VS2013 开发人员命令提示就可以使用CL、LINK、LIB、NMAKE这几个Windows下的编译和生成工具了。(相当于GNU的g++、ar、make)。

    编译ZThread

    从sourceforge.net下载ZThread-2.3.2.tar.gz,解压到F:/libs/ZThread-2.3.2

    查看README>查看BUILDING

    * Any other method is up to you. There are simply too many compilers,
    too many versions and too many platforms to maintain separate build files
    for.

    BUT, this doesn't mean you are out of luck.

    I have structured the code so that it is very simple to compile the library
    however suits your needs best. All you need to do is include the .cxx files
    in the src/ directory (not the subdirectories) in your build rule, and add the
    include directory to your compilers include/ path.

    打开terminal,切换到目录F:/libs/ZThread-2.3.2/src

    输入命令:CL /c /I..include *.cxx

      也就是编译所有的cxx文件,并生成对应的obj文件,但是不调用LINK对obj文件进行链接(比如somebody.cxx会被编译为somebody.obj)

      等价于GNU的:g++ -c -I ../include *.cxx

    然后输入命令:LIB /OUT:ZThread_win32.lib *.obj

      也就是将所有的obj文件打包为lib文件

      等价于GNU的:ar -r ZThread.a *.o

      注意此处必须用CL编译的obj文件,并且由LIB打包成lib文件,如果是其他编译器编译的.a文件,你改后缀名改成.lib在windows下是用不了的,会报很多undefined reference to xxx错误。总而言之你坚持一个原则:用GNU的工具生成的库,那么引用这个库的代码就必须用GNU的编译器来编译,如果是用windows的工具生成的库,那么引用这个库的代码就必须用windows的编译器来编译!一般来说,如果一个库只使用C++ Standard里面的东西,就是平台无关的,比如ZThread就是这样的库,这样的库在几乎任何支持C++ Standard的平台上都是可以编译的,只不过,你选择了哪个平台,你最好就用哪个平台的编译器!

    好,第二步,写代码,代码结构如下(加粗的就代表文件夹)

    zthreaddemo

      libs

        zthread

          ZThread_win32.lib

      include

        zthread

          // zthread的所有头文件

        LiftOff.h

        Test.h

      src

        LiftOff.cpp

        Test.cpp

        main.cpp

      Makefile

    代码我就不贴了,只贴Makefile的,你可以下载项目文件夹来看看

    Makefile

    # See
    # http://msdn.microsoft.com/zh-cn/library/f35ctcxw.aspx
    # for more info about Microsoft Visual C++ Compiler and Linker options
    
    # Microsoft Visual C++ Compiler && Linker tool
    CC = cl
    # Microsoft Visual C++ Linker
    LINK = link
    
    # LIB_ZTHREAD is the ZThread static link library path
    # /LIBPATH:<dir> Specifies a path that the linker will search before it searches the path specified in the LIB environment option. If you want to specify more than one directory, you must specify multiple /LIBPATH options.
    # here we need the ZThread_win32.lib
    LIB_ZTHREAD = /LIBPATH:libszthread
    
    # tells the linker where to find object files
    OBJ_PATH = /LIBPATH:obj
    
    # '/I<PATH_NAME>' or '/I <PATH_NAME>' specifies a header search path
    # for example: CL /I xxinclude /Imyinclude main.c
    # tells the compiler where to find my own header files
    # *** This makes you free from the burden to write things like #include "../include/xxx" in your cpp files ***
    HEADER_PATH = /I include
    
    # See this page, search '/EHsc'
    # http://wenku.baidu.com/view/04a34101de80d4d8d15a4ff2.html
    EHSC = /EHsc
    
    # Compiles without linking.
    COMPILATION_ONLY = /c
    
    # Compiler output object file: /Fo<PATH_NAME>, for example:
    # cl /c hello.cpp /Foobjhello.obj put the hello.obj file into the folder 'obj'
    C_OUT = /Fo:
    
    # Linker output executable file
    # note that the comma must be followed by the path WITHOUT any white-characters
    L_OUT = /OUT:
    
    bin	est.exe: bin obj objmain.obj objTest.obj objLiftOff.obj
        $(LINK) $(LIB_ZTHREAD) $(OBJ_PATH) $(L_OUT)bin	est.exe main.obj Test.obj LiftOff.obj zthread_win32.lib
    objmain.obj: srcmain.cpp includeTest.h
        $(CC) $(EHSC) $(HEADER_PATH) $(COMPILATION_ONLY) srcmain.cpp $(C_OUT)objmain.obj
        
    objTest.obj: srcTest.cpp includeTest.h
        $(CC) $(EHSC) $(HEADER_PATH) $(COMPILATION_ONLY) srcTest.cpp $(C_OUT)objTest.obj
    
        
    objLiftOff.obj: srcLiftOff.cpp includeLiftOff.h
        $(CC) $(EHSC) $(HEADER_PATH) $(COMPILATION_ONLY) srcLiftOff.cpp $(C_OUT)objLiftOff.obj
    
    obj:
        mkdir obj
        
    bin:
        mkdir bin
    # PHONY means 'clean' is a fake target
    # use 'make clean' to remove all .obj files
    # before rebuilding
    # '-' means continue execute next command even if something goes wrong with this command
    # type 'help' to get info about 'rmdir'
    # type 'help rmdir' to get info about '/s' and '/q'
    .PHONY: clean
    clean:
        -rmdir /s /q bin
        -rmdir /s /q obj

    这里面写了一些注释,看看可以帮助理解。总的思想就是:

    先创建bin和obj文件夹,调用CL编译src里面的cpp文件,把生成的obj文件放到obj文件夹里,接着调用LINK链接所有的obj文件和lib文件,生成test.exe放到bin目录下.

    要生成代码,所要做的工作就是打开VS2013 开发人员命令提示,然后切换到zthreaddemo目录中,输入命令nmake即可

    里面有几个要点:

    1、我将很多编译器和连接器的选项都写成了宏调用的形式,希望看起来可读性更好一些

    2、注意文件的组织方式,src、include、libs、obj、bin这样的组织方式更整洁,src里面根据你的模块要求还可以设置子文件夹,include也是,libs里面根据你的需要也可以设置子文件夹,obj的组织结构一般与src是一一对应的(也就是说,如果你有src/xxx/yyy.cpp,那么生成obj文件的时候也应该有obj/xxx/yyy.obj)

    3、类似于/OUT:这样的选项,冒号后面必须紧跟路径,中间不能有空格,而/I这个选项与路径之间,可以有空格也可以没空格

    Linux: (Ubuntu)

    编译ZThread,查看README, BUILDING

    一般来说在类Unix环境下安装一个库的流程都是先./configure,再make,再make install。这样就可以

    把头文件安装到/usr/local/include

    把库文件(.a文件)安装到/usr/local/lib

    把可执行文件安装到/usr/local/bin

    但是我不知道是我的问题还是ZThread的configure代码的问题,./configure总是报错

    所以我还是采取直接编译的方法,到/ZThread-2.3.2/src下运行命令g++ -I../include -fpermissive -c *.cxx

    选项-fpermissive的作用是把一些error给改成warning.原因是ZThread的代码是比较陈旧的代码了,在目前的C++ Standard中过去的某些语法可能被编译器视为error,但是-fpermissive选项的意思就是告诉编译器:我确定这个代码格式没有语法或其他错误,只是比较陈旧不符合现代的标准而已。可以参考这个解释

    选项-I../include是说zthread的头文件在上一级目录的include文件夹中

    然后运行命令ar -r ZThread.a *.o

    得到zthread.a

    文件的组织方式仍然如前面所述

    zthreaddemo

      libs

        zthread

          ZThread.a

      include

        zthread

          // zthread的所有头文件

        LiftOff.h

        Test.h

      src

        LiftOff.cpp

        Test.cpp

        main.cpp

      Makefile

    下载项目文件夹

    代码不变,只修改Makefile,如下所示(注意这里的Makefile与项目文件夹里的不太一样,以这里的为准)

    # See
    # http://www.gnu.org/software/gcc
    # for more info about GNU C++ Compiler and Linker options
    
    # GNU C++ Compiler && Linker tool
    CC = g++
    # GNU C++ Linker
    LINK = g++
    
    # LIB_PATH tells the linker where to find library files
    # -L <path> Specifies a path that the linker will search before it searches the path specified in the LIB environment option. If you want to specify more than one directory, you must specify multiple -L options.
    # here we need the ZThread.a
    LIB_PATH = -Llibs/zthread
    # -l<LIBRARY_NAME> specifies a library file to link
    # note that to use the '-l' flag, you must name
    # your static library file libLIBRARY_NAME.a (the prefix 'lib' 
    # cannot be omitted) and use '-Ldir'
    # before you actually using the '-l' flag, where 'dir'
    # is the directory that your libxxx.a can be found
    # immediately (can't be parent directory).
    # You might think that it's verbose to say '-Llibs/zthread -lZThread',
    # why not just say 'libs/zthread/ZThread.a' ?
    # Yes in this case it's not a good example. But what if
    # you put all your library files DIRECTLY in the folder 'libs'?
    # Suppose you put libraryA.a libraryB.a libraryC.a DIRECTLY
    # in the folder 'libs', you just need to specify '-Llibs' once,
    # then you just say '-llibraryA' '-llibraryB' '-libraryC' to link
    # all three library files.
    LIB_FILE = -l
    
    # '-I<PATH_NAME>' specifies a header search path
    # for example: g++ -I /xxinclude -I /my/include main.c
    # tells the compiler where to find my own header files
    # *** This makes you free from the burden to write things like #include "../include/xxx" in your cpp files ***
    HEADER_PATH = -Iinclude
    
    # Compiles without linking.
    COMPILATION_ONLY = -c
    
    # Compiler output object file: -o <PATH_NAME/object_file.o>, for example:
    # g++ -c hello.cpp -o obj/hello.o put the hello.o file into the folder 'obj'
    C_OUT = -o
    
    # Linker output executable file
    L_OUT = -o
    
    
    bin/test: bin obj obj/main.o obj/Test.o obj/LiftOff.o
        $(LINK) obj/main.o obj/Test.o obj/LiftOff.o $(LIB_PATH) -lZThread -lpthread $(L_OUT) bin/test
    obj/main.o: src/main.cpp include/Test.h
        $(CC) $(HEADER_PATH) $(COMPILATION_ONLY) src/main.cpp $(C_OUT) obj/main.o
        
    obj/Test.o: src/Test.cpp include/Test.h
        $(CC) $(HEADER_PATH) $(COMPILATION_ONLY) src/Test.cpp $(C_OUT) obj/Test.o
    
        
    obj/LiftOff.o: src/LiftOff.cpp include/LiftOff.h
        $(CC) $(HEADER_PATH) $(COMPILATION_ONLY) src/LiftOff.cpp $(C_OUT) obj/LiftOff.o
    
    obj:
        mkdir obj
        
    bin:
        mkdir bin
    # PHONY means 'clean' is a fake target
    # use 'make clean' to remove all .obj files
    # before rebuilding
    # '-' means continue execute next command even if something goes wrong with this command
    # type 'rm --help' to get info about '-r' and '-f'
    .PHONY: clean
    clean:
        -rm -r -f bin
        -rm -r -f obj

    ---------------------------------------

    2014/12/18更新之前的内容(已陈旧,有部分误区。尽量别看这个)

    用mingw32-make就行了,语法跟GNU make基本上是一样的,只是要针对windows写命令,比如linux下的rm指令(删除文件)在windows下需要换成del指令

    为什么不用Cygwin?——老爱报些莫名其妙的错误。下面举个例子

    下面用LIB_ZTHREAD代指Windows下的F:/libs/zthread_win32.a或者Ubuntu下的/home/admin/libs/zthread.a

    用HEADER_ZTHREAD代指Windows下的F:/libs/ZThread-2.3.2/include或者Ubuntu下的/home/admin/libs/ZThread-2.3.2/include

    点此下载zthread_win32.a

    点此下载zthread.a

    文件结构:所有的.cpp文件.h文件Makefile都在一个文件夹里,假设其目录为TEST_DIR

    源代码:

    main.cpp

    1 #include "Test.h"
    2 
    3 using namespace std;
    4 
    5 int main()
    6 {
    7     Test::testLiftOff();
    8     return 0;
    9 }

    Test.h

     1 #ifndef TEST_H
     2 #define TEST_H
     3 
     4 
     5 class Test
     6 {
     7     public:
     8         static void testLiftOff();
     9 
    10     private:
    11         Test();
    12         ~Test();
    13 };
    14 
    15 #endif // TEST_H

    Test.cpp

     1 #include "Test.h"
     2 
     3 #include "LiftOff.h"
     4 
     5 #include <zthread/Thread.h>
     6 
     7 #include <iostream>       // std::cout
     8 
     9 void Test::testLiftOff()
    10 {
    11     using namespace ZThread;
    12 
    13     try {
    14         for (int i = 0; i < 5; ++i)
    15         {
    16             Thread th(new LiftOff(10, i));
    17         }
    18         std::cout << "waiting for lift off" << std::endl;
    19     } catch (Synchronization_Exception &e) {
    20         std::cerr << e.what() << std::endl;
    21     }
    22 }
    23 
    24 Test::Test()
    25 {
    26     //ctor
    27 }
    28 
    29 Test::~Test()
    30 {
    31     //dtor
    32 }

    LiftOff.h

     1 #ifndef LIFTOFF_H
     2 #define LIFTOFF_H
     3 
     4 #include <zthread/Runnable.h>
     5 
     6 class LiftOff : public ZThread::Runnable
     7 {
     8     public:
     9         LiftOff(int countDown_, int id_);
    10         ~LiftOff();
    11         void run();
    12     private:
    13         int countDown;
    14         int id;
    15 };
    16 
    17 #endif // LIFTOFF_H

    LiftOff.cpp

     1 #include "LiftOff.h"
     2 
     3 #include <iostream>
     4 
     5 using namespace std;
     6 
     7 LiftOff::LiftOff(int countDown_, int id_)
     8     :countDown(countDown_), id(id_)
     9 {
    10     // do nothing
    11 }
    12 
    13 LiftOff::~LiftOff()
    14 {
    15     cout << "LiftOff" << id << " destroyed" << endl;
    16 }
    17 
    18 void LiftOff::run()
    19 {
    20     while (countDown--)
    21         cout << id << " count: " << countDown << endl;
    22     cout << id << "LiftOff!" << endl;
    23 }

    1. Ubuntu (linux) + GNU make

    Makefile

     1 # ZTHREAD_A the static link library file of ZThread
     2 ZTHREAD_A = /home/admin/libs/zthread.a 3 # ZTHREAD_H is the directory that has all the header
     4 # files of the ZThread library
     5 ZTHREAD_H = /home/admin/libs/ZThread-2.3.2/include 6 
     7 test.exe: main.o Test.o LiftOff.o
     8     g++ -o test.exe main.o Test.o LiftOff.o -s $(ZTHREAD_A) -lpthread # -lpthread is necessary to link pthread library, which is not part of the default library in Ubuntu, ZThread need pthread support
     9 main.o: main.cpp
    10     g++ -c main.cpp -o main.o
    11 # '-I' specifies the header search directory
    12 Test.o: Test.cpp Test.h
    13     g++ -I $(ZTHREAD_H) -c Test.cpp -o Test.o
    14 LiftOff.o: LiftOff.cpp LiftOff.h
    15     g++ -I $(ZTHREAD_H) -c LiftOff.cpp -o LiftOff.o
    16 
    17 # PHONY means 'clean' is a fake target
    18 # use 'make clean' to remove all .o files
    19 # before rebuilding
    20 .PHONY: clean
    21 clean:
    22     -rm test # '-' means continue execute next command even if something goes wrong
    23     -rm *.o

    make clean

    make -f Makefile

    运行成功

    2. Windows + mingw32-make

    Makefile

     1 # ZTHREAD_A the static link library file of ZThread
     2 ZTHREAD_A = F:/libs/ZThread-2.3.2/lib/zthread_win32.a
     3 # ZTHREAD_H is the directory that has all the header
     4 # files of the ZThread library
     5 ZTHREAD_H = F:/libs/ZThread-2.3.2/include
     6 
     7 test.exe: main.o Test.o LiftOff.o
     8     g++ -o test.exe main.o Test.o LiftOff.o -s $(ZTHREAD_A)
     9 main.o: main.cpp
    10     g++ -c main.cpp -o main.o
    11 # '-I' specifies the header search directory
    12 Test.o: Test.cpp Test.h
    13     g++ -I $(ZTHREAD_H) -c Test.cpp -o Test.o
    14 LiftOff.o: LiftOff.cpp LiftOff.h
    15     g++ -I $(ZTHREAD_H) -c LiftOff.cpp -o LiftOff.o
    16 
    17 # PHONY means 'clean' is a fake target
    18 # use 'make clean' to remove all .o files
    19 # before rebuilding
    20 # '-' means continue execute next command even if something goes wrong with this command
    21 .PHONY: clean
    22 clean:
    23     -del test.exe
    24     -del *.o

    mingw32-make clean

    mingw32-make -f Makefile

    运行成功

    3. Windows + Cygwin

    Makefile

     1 # ZTHREAD_A the static link library file of ZThread
     2 ZTHREAD_A = F:/libs/ZThread-2.3.2/lib/zthread_win32.a
     3 # ZTHREAD_H is the directory that has all the header
     4 # files of the ZThread library
     5 ZTHREAD_H = F:/libs/ZThread-2.3.2/include
     6 
     7 test.exe: main.o Test.o LiftOff.o
     8     g++ -o test.exe main.o Test.o LiftOff.o -s $(ZTHREAD_A)
     9 main.o: main.cpp
    10     g++ -c main.cpp -o main.o
    11 # '-I' specifies the header search directory
    12 Test.o: Test.cpp Test.h
    13     g++ -I $(ZTHREAD_H) -c Test.cpp -o Test.o
    14 LiftOff.o: LiftOff.cpp LiftOff.h
    15     g++ -I $(ZTHREAD_H) -c LiftOff.cpp -o LiftOff.o
    16 
    17 # PHONY means 'clean' is a fake target
    18 # use 'make clean' to remove all .o files
    19 # before rebuilding
    20 # '-' means continue execute next command even if something goes wrong with this command 
    21 .PHONY: clean
    22 clean:
    23     -rm test.exe
    24     -rm *.o

    make clean没问题

    make报错(貌似是找不到__assert的实现,真心无语),报错的详细信息见这篇随笔

  • 相关阅读:
    如何从程序集中加载及卸载插件(下)
    Castle AOP 系列(四):实现一个简单的基于上下文调用的权限校验机制
    Castle AOP 系列(一):对类方法调用的拦截
    Mussel使用系列(六):分布式调用的牛刀小试
    新发现XmlNode中变态的地方
    我们发现你在XX邮箱的账号密码与其他网站被盗账号密码一致 请立即更改密码。
    html5 css3 新特性一览表
    [android] Http Post 请求
    [vs 使用技巧] VS2013显示行数 行号
    ORACLE数据库存储使用情况查询命令
  • 原文地址:https://www.cnblogs.com/qrlozte/p/4139322.html
Copyright © 2020-2023  润新知