【问题标题】:Link Waf target to a library generated by external build system (CMake)将 Waf 目标链接到由外部构建系统 (CMake) 生成的库
【发布时间】:2015-12-03 02:33:29
【问题描述】:

我的 waf 项目有两个依赖项,使用 CMake 构建。 我正在尝试做的是按照 waf git repo 中的dynamic_build3 示例,创建一个生成 CMake 的工具,并在成功构建后,在 waf 的输出子目录中执行安装:

@extension('.txt')
def spawn_cmake(self, node):
    if node.name == 'CMakeLists.txt':
        self.cmake_task = self.create_task('CMake', node)
        self.cmake_task.name = self.target


@feature('cmake')
@after_method('process_source')
def update_outputs(self):
    self.cmake_task.add_target()


class CMake(Task.Task):
    color = 'PINK'

    def keyword(self):
        return 'CMake'

    def run(self):
        lists_file = self.generator.source[0]
        bld_dir = self.generator.bld.bldnode.make_node(self.name)
        bld_dir.mkdir()

        # process args and append install prefix
        try:
            cmake_args = self.generator.cmake_args
        except AttributeError:
            cmake_args = []
        cmake_args.append(
            '-DCMAKE_INSTALL_PREFIX={}'.format(bld_dir.abspath()))

        # execute CMake
        cmd = '{cmake} {args} {project_dir}'.format(
            cmake=self.env.get_flat('CMAKE'),
            args=' '.join(cmake_args),
            project_dir=lists_file.parent.abspath())
        try:
            self.generator.bld.cmd_and_log(
                cmd, cwd=bld_dir.abspath(), quiet=Context.BOTH)
        except WafError as err:
            return err.stderr

        # execute make install
        try:
            self.generator.bld.cmd_and_log(
                'make install', cwd=bld_dir.abspath(), quiet=Context.BOTH)
        except WafError as err:
            return err.stderr

        try:
            os.stat(self.outputs[0].abspath())
        except:
            return 'library {} does not exist'.format(self.outputs[0])

        # store the signature of the generated library to avoid re-running the
        # task without need
        self.generator.bld.raw_deps[self.uid()] = [self.signature()] + self.outputs

    def add_target(self):
        # override the outputs with the library file name
        name = self.name
        bld_dir = self.generator.bld.bldnode.make_node(name)
        lib_file = bld_dir.find_or_declare('lib/{}'.format(
            (
                self.env.cshlib_PATTERN
                if self.generator.lib_type == 'shared' else self.env.cstlib_PATTERN
            ) % name))
        self.set_outputs(lib_file)

    def runnable_status(self):
        ret = super(CMake, self).runnable_status()
        try:
            lst = self.generator.bld.raw_deps[self.uid()]
            if lst[0] != self.signature():
                raise Exception
            os.stat(lst[1].abspath())
            return Task.SKIP_ME
        except:
            return Task.RUN_ME
        return ret

我想生成该工具,然后将 waf 目标链接到已安装的库,我通过调用 bld.read_shlib() 使用“假库”机制执行此操作:

def build(bld):
    bld.post_mode = Build.POST_LAZY
    # build 3rd-party CMake dependencies first
    for lists_file in bld.env.CMAKE_LISTS:
        if 'Chipmunk2D' in lists_file:
            bld(
                source=lists_file,
                features='cmake',
                target='chipmunk',
                lib_type='shared',
                cmake_args=[
                    '-DBUILD_DEMOS=OFF',
                    '-DINSTALL_DEMOS=OFF',
                    '-DBUILD_SHARED=ON',
                    '-DBUILD_STATIC=OFF',
                    '-DINSTALL_STATIC=OFF',
                    '-Wno-dev',
                ])
    bld.add_group()

    # after this, specifying `use=['chipmunk']` in the target does the job
    out_dir = bld.bldnode.make_node('chipmunk')
    bld.read_shlib(
        'chipmunk',
        paths=[out_dir.make_node('lib')],
        export_includes=[out_dir.make_node('include')])

我觉得这个 * 非常难看 * 因为:

  1. 在最终目标的链接阶段需要花栗鼠库,没有理由阻止整个构建(通过使用Build.POST_LAZY 模式和bld.add_group()),尽管取消阻止它会使read_shlib()失败。想象一下,如果在那之前还有某种git clone 任务......
  2. build() 命令中调用read_shlib() 意味着调用者知道该工具安装文件的方式和位置。我希望工具本身执行对read_shlib() 的调用(如果有必要的话)。但是我在run()runnable_status() 中没有这样做,正如Waf Book 部分关于Custom tasks 的第11.4.2 段所建议的那样,似乎我必须以某种方式封装在ANOTHER 中对read_shlib() 的调用 任务并将其放在 undocumented more_tasks 属性中。

还有问题:

  1. 如何将 read_shlib() 调用封装在任务中,以由 CMake 任务生成?
  2. 是否可以让任务以非阻塞方式并行执行其他任务(假设项目有 2 或 3 个这些 CMake 依赖项,这些依赖项将由git 从远程存储库获取)?

【问题讨论】:

    标签: python c++ c cmake waf


    【解决方案1】:

    其实你已经完成了大部分工作:)

    read_shlib 只创建一个假任务,假装构建一个已经存在的库。在您的情况下,您确实构建了库,因此您真的不需要read_shlib。只要您设置了正确的参数,您就可以在某处 use 您的 cmake 任务生成器。

    关键字use 识别使用的任务生成器中的一些参数:

    • export_includes
    • export_defines

    如果使用的任务生成器有link_task,它还管理库和任务顺序。

    因此,您只需在 cmake 任务生成器中正确设置 export_includesexport_defines,并设置引用您的 cmake_task 属性的 link_task 属性。您还必须正确设置您的 cmake_task 输出以使其正常工作,即列表的第一个输出必须是 lib 节点(您在 add_target 中所做的似乎没问题)。比如:

    @feature('cmake')
    @after_method('update_outputs')
    def export_for_use(self):
        self.link_task = self.cmake_task
        out_dir = self.bld.bldnode.make_node(self.target)
        self.export_includes = out_dir.make_node('include')
    

    完成后,您只需在主 wscript 中编写:

    def build(bld):
        for lists_file in bld.env.CMAKE_LISTS:
            if 'Chipmunk2D' in lists_file:
                bld(
                    source=lists_file,
                    features='cmake',
                    target='chipmunk',
                    lib_type='shared',
                    cmake_args=[
                        '-DBUILD_DEMOS=OFF',
                        '-DINSTALL_DEMOS=OFF',
                        '-DBUILD_SHARED=ON',
                        '-DBUILD_STATIC=OFF',
                        '-DINSTALL_STATIC=OFF',
                        '-Wno-dev',
                    ])
    
        bld.program(source="main.cpp", use="chipmunk")
    

    您当然可以简化/分解代码。我认为 add_target 不应该在任务中,它主要管理任务生成器属性。

    【讨论】:

      猜你喜欢
      • 2017-01-05
      • 1970-01-01
      • 2019-08-17
      • 2013-11-08
      • 1970-01-01
      • 2021-09-17
      相关资源
      最近更新 更多