zoukankan      html  css  js  c++  java
  • 怎么快速构建自己的C/C++程序?——有关编译、静态链接和SCons

    怎么快速构建自己的C/C++程序?——有关编译、静态链接和SCons

    1. 写在前面

    最初写C++是在Visual Studio这个IDE里,那时我并没有makefile的概念,对程序的编译和链接的一些概念也是比较模糊。在VS下,随便增加h/cpp文件,基本上按下编译运行只要不报错就能运行。

    后来开始尝试在linux平台写程序时,为避免编写makefile,曾经一度在Ubuntu里使用Code Block这个IDE。

    使用IDE也有很多不爽的地方。再后来我就习惯于在Win下用Sublime的文本编辑器写程序,因为它有很好的目录层级,然后扔到linux上去编译。发现不用makefile基本控制不了不断膨胀的代码,然而又发现即便用各种自动工具去生成makefile学习曲线依然有些陡峭。

    在探索cmake、autuomake的途中,了解到了SCons。这是一个可以用python去编写类似makefile的工具,它的使用难度相比cmake和automake都要简单不少。有了SCons这一神器,makefile便不再可怕,而是一件很简单的事情了。

    本文首先简单介绍有关编译和静态链接的相关知识,不去涉及更复杂的动态链接等知识,真的是简单介绍;再介绍如何用SCons简单且快速地去构建自己的程序。对编译和链接过程有所了解,可以在构建过程更清楚发生了什么,明白链接时报错的意思。

    2. 编译与静态链接

    2.1 编译Hello World

    当我们编写最简单的"Hello World"代码后,将它编译运行。

    #include <stdio.h>
    int main()
    {
        printf("Hello World");
        return 0;
    }
    
    gcc hello.c
    

    这时会经过四个步骤,分别是预处理、编译、汇编和链接。

    • 预处理:处理掉源代码文件中那些“#”号开始的预编译指令,删除掉注释等。
    • 编译:最复杂的部份,经过词法分析、语法分析、语义分析及优化产生相应的汇编语言文件。
    • 汇编:将汇编代码转换机器语言,生成可重定位目标文件,也就是构建过程中常见的.o文件。
    • 链接:将必要的目标文件组合起来,生成可执行文件。

    2.2 目标文件

    目标文件有三种:

    1. 可重定位目标文件。在Linux是常见的.o文件。包含二进制代码和数据,可以在链接时与其他可重定位目标文件合并,创建可执行目标文件。
    2. 可执行目标文件。包含了二进制代码,可以被拷贝至内存并执行。
    3. 共享目标文件。特殊类型的可重定位目标文件,可以在加载或者运行时被动态地加载到内存中并链接。

    在程序构建的过程,编译器和汇编器生成可重定位目标和共享目标文件,链接器生成可执行文件。它们在Linux下使用ELF格式存储,是可执行和可链接格式(Executable and Linkable Format, ELF)的缩写。

    2.2.1 可重定位目标文件

    目标文件中有变量定义在别的模块中。在C/C++中模块之间可以通过函数调用,可以去使用别的模块定义的全局变量。

    2.3 静态链接

    2.3.1 符号解析

    2.2.2 重定位

    3. 使用Scons构建自己的程序

    以下是scons编译panguan的脚本

    #!/usr/bin/scons -f
    # coding:utf-8
    import os
    
    process_name = 'panguan-v0.1'
    
    def all_dir(ori_dir):
        src_set = []
        for root, dirs, files in os.walk(ori_dir):
            for dir in dirs:
                path = os.path.join(root, dir)
                src_set.append(path)
        return src_set
    
    def all_src_file(dir):
        src_set = []
        for root, dirs, files in os.walk(dir):
            for file in files:
                path = os.path.join(root, file)
                if file.lower().endswith(('.cpp', '.c')):
                    src_set.append(path)
        return src_set
    
    def main():
        env = Environment(ENV=os.environ)
    
        # add the gcc complie(above 4.8) pos
        env.PrependENVPath('PATH', '/usr/local/gcc-4.8.5/bin')
        env.PrependENVPath('LIBRARY_PATH', '/usr/local/gcc-4.8.5/lib64')
        env.PrependENVPath('CPLUS_INCLUDE_PATH',
            '/usr/local/gcc-4.8.5/include/c++/4.8.5')
        
        # add the nanomsg lib pos
        # env.PrependENVPath('CPLUS_INCLUDE_PATH', 'lib/include')
        # env.PrependENVPath('LIBRARY_PATH', 'lib/lib')
        # env.PrependENVPath('LD_LIBRARY_PATH', 'lib/lib')
    
        # modify argues
        env.Replace(CCFLAGS='-Wl,--no-as-needed -std=c++11 -Wall -g -Wno-reorder -lpthread -pthread')
        env.Replace(LINKFLAGS='-Wl,--no-as-needed -std=c++11 -Wall -g -Wno-reorder -lpthread -pthread')
        header_path = []
        header_path += all_dir('common')
        env.Prepend(CPPPATH=header_path)
    
        common_src = []
        common_src += all_src_file('common')
        env.StaticLibrary('common/common', common_src)
        env.Prepend(LIBPATH=['common'])
        env.Prepend(LIBS=['common'])
    
        header_path += all_dir('src')
        env.Prepend(CPPPATH=header_path)
    
        src_file = []
        src_file += all_src_file('src')
        env.Program(process_name, src_file)
    
    if __name__ != '__main__':
        main()
    
    
  • 相关阅读:
    1.8.4- 默认选中表单属性
    1.8.3- 单选框和复选按钮
    1.8.2- 文本框和密码
    springboot整合logback集成elk实现日志的汇总、分析、统计和检索功能
    elasticsearch kibana logstash(ELK)的安装集成应用
    sslopen RSA加解密
    Docker基本使用运行ngix镜像
    springCloud 之 Eureka注册中心高可用配置
    springCloud 之 Eureka服务治理
    springboot整合redis
  • 原文地址:https://www.cnblogs.com/ohmhong/p/6870009.html
Copyright © 2011-2022 走看看