You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Makefile中使用数值变量?实现带零填充的软盘镜像生成

在Makefile中使用数值变量生成软盘镜像

我来帮你搞定这个问题!要在Makefile里实现文件对齐、动态计算写入位置,核心是结合Make的变量处理能力和Shell的整数运算,下面是完整的解决方案和细节说明:

核心思路

  • 定义常量(扇区大小、软盘总容量)来统一管理数值,避免硬编码。
  • 用stat命令获取文件实际大小,通过Shell的向上取整技巧计算文件需要占用的扇区数(确保对齐到512字节)。
  • 动态维护当前写入的扇区位置,每次写完一个文件后更新这个位置,避免覆盖之前的内容。
  • 用dd的seek参数指定写入起始扇区,配合conv=sync,notrunc实现对齐和追加写入。

完整的Makefile示例

# 常量定义:512字节扇区,1.44MB软盘总大小
SECTOR_SIZE := 512
FLOPPY_SIZE := 1474560

# 初始写入位置:扇区0(留给bootloader)
START_LOCATION := 0

build: result

# 先创建空的软盘镜像,预填充到指定大小
result:
	@echo "Initializing empty floppy image..."
	dd if=/dev/zero of=$@ bs=$(FLOPPY_SIZE) count=1 conv=sync

# 写入bootloader到扇区0(假设bootloader不超过512字节)
	@echo "Writing bootloader to sector $(START_LOCATION)..."
	dd if=bootloader of=$@ bs=$(SECTOR_SIZE) conv=sync,notrunc
	$(eval CURRENT_LOC := $(shell echo $$(($(START_LOCATION) + 1))))

# 定义复用函数:处理单个文件的写入和位置更新
define write_file
	@echo "Processing $(1)..."
	$(eval FILE_SIZE := $(shell stat -c%s $(1)))
	$(eval FILE_SECTORS := $(shell echo $$(( ($(FILE_SIZE) + $(SECTOR_SIZE) - 1) / $(SECTOR_SIZE) )) ))
	dd if=$(1) of=result bs=$(SECTOR_SIZE) conv=sync,notrunc seek=$(CURRENT_LOC)
	$(eval CURRENT_LOC := $(shell echo $$(($(CURRENT_LOC) + $(FILE_SECTORS)))))
endef

# 调用函数写入file1和file2
	$(call write_file, file1)
	$(call write_file, file2)

# 可选:输出验证信息
	@echo "Done! Total sectors used: $(CURRENT_LOC)"
	@echo "Final image size: $(shell stat -c%s result) bytes"

clean:
	rm -f result

关键细节解释

  • 向上取整计算扇区数:$(( ($(FILE_SIZE) + $(SECTOR_SIZE) - 1) / $(SECTOR_SIZE) ))是Shell里的经典技巧,比如文件大小是600字节,计算后会得到2个扇区(600+512-1=1111,1111/512=2),确保文件被自动填充到完整扇区。
  • 动态更新位置:用$(eval)命令在Makefile执行过程中实时更新CURRENT_LOC变量——因为Make默认是静态解析变量,eval可以让我们动态修改数值,实现位置的累加。
  • dd参数说明:
    • seek=$(CURRENT_LOC):指定从镜像的第N个扇区开始写入,跳过前面已写入的内容。
    • conv=sync:自动用零填充文件到bs指定的块大小(这里是512字节)。
    • notrunc:保证写入时不会截断整个镜像文件,只会覆盖指定位置的内容。
  • 跨系统兼容:如果是在macOS上,stat命令的参数不同,需要把stat -c%s改成stat -f%z,可以加个条件判断兼容不同系统:
    ifeq ($(shell uname), Darwin)
      STAT_SIZE := stat -f%z
    else
      STAT_SIZE := stat -c%s
    endif
    # 然后用$(STAT_SIZE)代替stat -c%s
    $(eval FILE_SIZE := $(shell $(STAT_SIZE) $(1)))
    

优化建议

如果要处理更多文件,可以把文件名放到一个变量里,用循环批量处理:

FILES := file1 file2 file3

# 然后在build里循环调用函数
	@for file in $(FILES); do \
		$(call write_file, $$file); \
	done

内容的提问来源于stack exchange,提问作者Virgil Ming

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 08:01:42