Building System之 get_abs_build_var() && get_build_var()

本文介绍了Building System中的两个关键函数get_abs_build_var()和get_build_var(),它们都位于build/envsetup.sh。这两个shell函数主要在config.mk和dumpvar.mk文件中操作,用于获取和打印构建系统的变量值。例如:ANDROID_GCC_PREBUILTS, PRODUCT_OUT, HOST_OUT等。通过对config.mk和dumpvar.mk的分析,揭示了这些变量如何定义和使用。" 81231778,7354457,决策树算法详解:理论+图解+Python实现,"['机器学习', '数据挖掘', '决策树算法']

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


1、get_abs_build_var() 和 get_build_var()的实现都在build/envsetup.sh中。

2、在buld目录下grep这两个函数可知:这两个函数只在build/envsetup.sh脚本中使用。

言归正传,贴代码

# Get the value of a build variable as an absolute path.
function get_abs_build_var()
{
    T=$(gettop)
    if [ ! "$T" ]; then
        echo "Couldn't locate the top of the tree.  Try setting TOP." >&2
        return
    fi
    (\cd $T; CALLED_FROM_SETUP=true BUILD_SYSTEM=build/core \
      command make --no-print-directory -f build/core/config.mk dumpvar-abs-$1)
}

# Get the exact value of a build variable.
function get_build_var()
{
    T=$(gettop)
    if [ ! "$T" ]; then
        echo "Couldn't locate the top of the tree.  Try setting TOP." >&2
        return
    fi
    (\cd $T; CALLED_FROM_SETUP=true BUILD_SYSTEM=build/core \
      command make --no-print-directory -f build/core/config.mk dumpvar-$1)
}

这两个shell函数的实现很相似,都是通过make指定build/core/config.mk文件,唯一不同的地方是target目标不同。

接下来我们分析config.mk文件。

config.mk文件开头的注释:

# This is included by the top-level Makefile.
# It sets up standard variables based on the
# current configuration and platform, which
# are not specific to what is being built.

根据注释,我们可知config.mk文件主要是配置变量。

而且,config.mk 文件最后一行包含的$(BUILD_SYSTEM)/dumpvar.mk用于获取一些变量。


接下来我们分析dumpvar.mk文件。

# ---------------------------------------------------------------
# the setpath shell function in envsetup.sh uses this to figure out
# what to add to the path given the config we have chosen.
ifeq ($(CALLED_FROM_SETUP),true) # 在get_abs_build_var() 和 get_build_var()函数中,CALLED_FROM_SETUP=true,走if分支。

ifneq ($(filter /%,$(HOST_OUT_EXECUTABLES)),) 
# HOST_OUT_EXECUTABLES变量是在envsetup.mk文件中设置的。
ABP:=$(HOST_OUT_EXECUTABLES)
else
ABP:=$(PWD)/$(HOST_OUT_EXECUTABLES)
endif

ANDROID_BUILD_PATHS := $(ABP)   # 应该是 out/host/linux-x86/bin
ANDROID_PREBUILTS := prebuilt/$(HOST_PREBUILT_TAG) 
# HOST_PREBUILT_TAG变量是在envsetup.mk文件中设置的。应该是 linux-x86
ANDROID_GCC_PREBUILTS := prebuilts/gcc/$(HOST_PREBUILT_TAG)

# The "dumpvar" stuff lets you say something like
#
#     CALLED_FROM_SETUP=true \
#       make -f config/envsetup.make dumpvar-TARGET_OUT
# or
#     CALLED_FROM_SETUP=true \
#       make -f config/envsetup.make dumpvar-abs-HOST_OUT_EXECUTABLES
#
# The plain (non-abs) version just dumps the value of the named variable.
# The "abs" version will treat the variable as a path, and dumps an
# absolute path to it.
#
dumpvar_goals := \
        $(strip $(patsubst dumpvar-%,%,$(filter dumpvar-%,$(MAKECMDGOALS)))) 
        # MAKECMDGOALS 变量就是传进来的目标dumpvar-abs-$1或 dumpvar-$1。
ifdef dumpvar_goals

  ifneq ($(words $(dumpvar_goals)),1)
    $(error Only one "dumpvar-" goal allowed. Saw "$(MAKECMDGOALS)")
  endif

  # If the goal is of the form "dumpvar-abs-VARNAME", then
  # treat VARNAME as a path and return the absolute path to it.
  absolute_dumpvar := $(strip $(filter abs-%,$(dumpvar_goals)))
  ifdef absolute_dumpvar
    dumpvar_goals := $(patsubst abs-%,%,$(dumpvar_goals))
    ifneq ($(filter /%,$($(dumpvar_goals))),)
      DUMPVAR_VALUE := $($(dumpvar_goals))
    else
      DUMPVAR_VALUE := $(PWD)/$($(dumpvar_goals))
    endif
    dumpvar_target := dumpvar-abs-$(dumpvar_goals)
  else
    DUMPVAR_VALUE := $($(dumpvar_goals))
    dumpvar_target := dumpvar-$(dumpvar_goals)
  endif

.PHONY: $(dumpvar_target)
$(dumpvar_target):
        @echo $(DUMPVAR_VALUE) 
        # DUMPVAR_VALE其实就是dumpvar-$1中的$1变量的值。

endif # dumpvar_goals

ifneq ($(dumpvar_goals),report_config) 
# PRIN
<think>我们讨论的是如何在Python中模拟Makefile的依赖规则。在Makefile中,`sbl: $(Lib_list)` 表示目标`sbl`依赖于变量`Lib_list`所代表的文件列表。当这些文件发生变化时,就会重新构建目标`sbl`。 在Python中,我们可以通过以下几种方式模拟这种依赖关系: 1. 使用构建工具(如SCons、Waf)或任务运行器(如doit、invoke)来定义任务和依赖关系。 2. 使用文件时间戳检查依赖关系,手动实现类似Makefile的规则。 3. 使用函数装饰器定义任务依赖(如pydoit的doit工具)。 下面我将分别展示这些方法。 ### 方法1:使用doit任务运行器 doit是一个基于Python的任务运行器,可以定义任务和它们的依赖关系。 安装doit: ```bash pip install doit ``` 创建一个`dodo.py`文件: ```python from doit.tools import config_changed # 假设Lib_list是一组文件 Lib_list = ['file1.c', 'file2.c'] def task_sbl(): """构建sbl目标""" return { 'file_dep': Lib_list, # 依赖的文件列表 'actions': ['gcc -c %(dependencies)s -o sbl'], # 构建命令 'targets': ['sbl'], # 生成的目标文件 # 可选:使用配置变化作为依赖 # 'uptodate': [config_changed({'flag': 'some_build_flag'})] } ``` 运行: ```bash doit sbl ``` 当`file1.c`或`file2.c`发生变化时,再次运行`doit sbl`就会重新构建。 ### 方法2:手动检查时间戳 我们可以手动检查依赖文件的时间戳是否比目标文件新,如果是则执行构建命令。 ```python import os import subprocess # 依赖文件和目标文件 Lib_list = ['file1.c', 'file2.c'] target = 'sbl' # 检查目标是否需要重建 def need_rebuild(target, deps): if not os.path.exists(target): return True target_time = os.path.getmtime(target) for dep in deps: if os.path.getmtime(dep) > target_time: return True return False if need_rebuild(target, Lib_list): # 构建命令 cmd = 'gcc -c {} -o {}'.format(' '.join(Lib_list), target) subprocess.run(cmd, shell=True) else: print(f'{target} is up to date.') ``` ### 方法3:使用类Makefile的构建工具(如SCons) SCons是一个使用Python脚本的构建工具。 安装SCons: ```bash pip install scons ``` 创建`SConstruct`文件: ```python # 定义环境 env = Environment() # 定义依赖和目标 Lib_list = ['file1.c', 'file2.c'] target = 'sbl' # 定义构建规则 env.Program(target, Lib_list) ``` 运行: ```bash scons ``` ### 方法4:使用函数和缓存(针对函数输出,而非文件) 如果依赖关系不是文件,而是函数的输出(即内存中的对象),我们可以使用缓存来避免重复计算。 ```python from functools import lru_cache # 假设Lib_list是几个函数的输出 @lru_cache(maxsize=None) def dep1(): # 计算一些东西 return result1 @lru_cache(maxsize=None) def dep2(): return result2 def sbl(): # 依赖dep1和dep2 d1 = dep1() d2 = dep2() # 使用d1和d2进行构建 result = ... return result # 当dep1或dep2的输入变化时,我们需要清除缓存,这样sbl就会重新计算。 # 清除缓存:dep1.cache_clear(), dep2.cache_clear() ``` 但是请注意,这种方法适用于纯函数,并且需要手动管理缓存清除。 ### 总结 根据不同的场景,可以选择不同的方法: - 如果构建过程涉及文件,推荐使用doit或SCons。 - 如果只是简单的文件依赖,也可以手动实现时间戳检查。 - 如果是函数间的依赖,可以使用缓存机制(但要小心管理缓存)。 在Python生态中,有多个工具可以模拟Makefile的依赖规则,选择哪个取决于具体需求。 ### 相关问题 1. 如何在Python中监控文件变化并自动触发任务? 2. 除了doit和SCons,还有哪些Python构建工具? 3. 如何管理Python函数之间的依赖关系? 4. 在大型项目中如何组织构建规则?</think>在 Python 中实现类似 Makefile 的依赖规则(如 `sbl: $(Lib_list)`),可以通过以下几种方法实现: --- ### 方法 1:使用 `make` 模块(最接近 Makefile 的语法) ```python from make import rule # 定义依赖规则 @rule(target='sbl', dependencies=['file1.py', 'file2.py', 'lib/common.py']) def build_sbl(): """构建 sbl 目标的命令""" print("Building sbl target...") # 执行实际构建命令 # 例如: os.system("python build_script.py") # 执行构建 if __name__ == "__main__": build_sbl() ``` **特点**: 1. 使用装饰器 `@rule` 声明目标和依赖项 2. 自动检查依赖项的时间戳,仅当依赖项更新时执行构建 3. 语法最接近 Makefile(需要安装 `pymake` 包) --- ### 方法 2:使用 `doit` 任务运行器(推荐用于复杂项目) ```python from doit import get_var def task_sbl(): """构建 sbl 目标""" lib_list = ['file1.py', 'file2.py', 'lib/common.py'] return { 'file_dep': lib_list, # 依赖文件列表 'targets': ['sbl'], # 输出目标 'actions': [ 'echo Building sbl target...', 'python build_script.py' # 实际构建命令 ], 'uptodate': [True], # 仅当依赖变更时执行 } ``` **执行方式**: ```bash doit sbl # 执行特定任务 doit # 执行所有任务 ``` **优势**: - 支持文件依赖检查 - 可定义任务间的依赖关系 - 支持增量构建 --- ### 方法 3:手动实现依赖检查 ```python import os import glob import hashlib class BuildSystem: def __init__(self): self.dependency_map = {} def add_rule(self, target, dependencies, action): """添加构建规则""" self.dependency_map[target] = { 'deps': dependencies, 'action': action } def needs_rebuild(self, target): """检查是否需要重建""" if not os.path.exists(target): return True target_time = os.path.getmtime(target) for dep in self.dependency_map[target]['deps']: if os.path.getmtime(dep) > target_time: return True return False def build(self, target): """执行构建""" if self.needs_rebuild(target): print(f"Building {target}...") self.dependency_map[target]['action']() else: print(f"{target} is up to date") # 使用示例 build_sys = BuildSystem() build_sys.add_rule( target='sbl', dependencies=glob.glob('src/*.py') + ['lib/common.py'], action=lambda: os.system("python build.py") ) build_sys.build('sbl') ``` --- ### 方法 4:使用 `luigi` 工作流引擎(适合复杂流水线) ```python import luigi class LibTask(luigi.ExternalTask): """表示依赖项""" def output(self): return luigi.LocalTarget('lib_list.txt') class SBuild(luigi.Task): """构建 sbl 目标""" def requires(self): return LibTask() # 声明依赖 def output(self): return luigi.LocalTarget('sbl_output') def run(self): with self.input().open('r') as f: libs = f.read().splitlines() # 使用依赖项执行构建 print(f"Building with {len(libs)} dependencies...") with self.output().open('w') as out: out.write("Build complete") if __name__ == "__main__": luigi.build([SBuild()], local_scheduler=True) ``` --- ### 关键概念对比 | 方法 | 优势 | 适用场景 | 安装依赖 | |------|------|----------|----------| | `make` 模块 | 语法接近Makefile | 简单构建任务 | `pip install pymake` | | `doit` | 强大的任务管理 | 中型项目构建 | `pip install doit` | | 手动实现 | 零依赖 | 小型自定义需求 | 无 | | `luigi` | 复杂工作流支持 | 数据流水线任务 | `pip install luigi` | --- ### 典型应用场景 1. **文件处理流水线**: ```python @rule(target='report.pdf', dependencies=['data.csv', 'template.tex']) def generate_report(): os.system("pdflatex template.tex") ``` 2. **编译任务**: ```python @rule(target='program.exe', dependencies=['main.c', 'utils.c']) def compile_program(): os.system("gcc -o program.exe main.c utils.c") ``` 3. **数据处理任务**: ```python def task_process_data(): return { 'file_dep': ['raw_data.csv'], 'targets': ['processed_data.pkl'], 'actions': ['python clean_data.py'] } ``` --- ### 常见问题解决 1. **处理动态依赖**(如引用[2]中的-fPIC问题): ```python def get_dynamic_deps(): return [f for f in os.listdir('src') if f.endswith('.c')] @rule(target='lib.so', dependencies=get_dynamic_deps) def build_lib(): os.system("gcc -fPIC -shared -o lib.so src/*.c") ``` 2. **处理Python模块依赖**(如引用[3]): ```python @rule(target='venv/.installed', dependencies=['requirements.txt']) def install_deps(): os.system("python -m pip install -r requirements.txt") touch('venv/.installed') # 创建标记文件 ``` --- ### 相关问题 1. 如何在Python中实现类似Makefile的自动变量(如`$@`, `$<`)? 2. 如何处理Python项目中的跨平台构建依赖问题? 3. 如何优化大型Python项目的增量构建性能? 4. 在持续集成(CI)环境中如何有效管理构建依赖? 5. Python构建系统与Makefile相比有哪些优势和局限?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值