类似Arduino的Makefile与依赖…?

我目前正在尝试使用SDCC OS编译器为STM8微控制器开发C库和项目模板。 我的目标是一个(几乎)与NOdu兼容的设置,类似于Arduino – 但是使用make + shellscripts而不是IDE(我的野心有限……)

目前我正在努力使用make来自动检测依赖项。 在Arduino中,用户只包含相关的头文件,例如“#include LCD-lib”,构建机制自动检测依赖关系并链接各自的库。 无需手动将其添加到IDE或Makefile。

我喜欢它的简单性,但到目前为止,我在创建相应的Makefile时失败了。 基本上这是Makefile应该实现的:

  1. 扫描项目根目录中的* .c文件以获取包含的标头。 请注意,这些文件位于不同的lib文件夹中
  2. 将所有包含的标头和(如果存在)相应的C文件添加到构建过程
  3. 为了最小化编译时间和大小,必须在构建期间跳过lib文件夹中未使用的C文件

我相信make可以做到以上所有 – 但不是我的制作经验…… 🙁

这是我想到的文件夹结构:

├── Library │  ├── Base │  │  ├── general STM8 sources and headers │  ├── STM8S_Discovery │  │  └── board specific sources and headers │  └── User │    └── optional user library sources and headers ├── Projects │  ├── Examples (to be filled) │  │  └── Basic_Project │  │  ├── compile_upload.sh --> double-click to build and upload │  │  ├── config.h │  │  ├── main.c │  │  └── Makefile --> should detect dependencies in ./*.c and ./*.h │  └── User_Projects (still empty) └── Tools ├── programmer.py --> for programming (already works from make) └── terminal.py --> for serial terminal (already works from make) 

我知道要问很多,但方便的Makefile是我的主要阻塞点。 任何帮助都非常感谢!!! 非常感谢提前!

此致,Georg Icking-Konert

注意:我意识到这个答案并不符合您的所有要求,实际上这种方法仍然要求您列出您在项目中使用的相关Arduino库的名称,以及应该是目录的路径列表。包含在项目中。 但是,这个解决方案是我能想到的最接近你的要求的,它可能仍然可以帮助其他人在路上阅读这个问题。


我使用Arduino Makefile

  1. Makefile.master放在主工作空间目录中
  2. 当您启动一个新的Arduino项目时,您将其创建为工作区中的子目录
    • 使用.pde/.ino扩展名创建一个包含setup()和`loop()方法的文件
    • 将剩余的逻辑放入.c/.cpp/.h/.hpp文件中
  3. 添加一个项目Makefile ,在该子目录中设置项目精炼设置,例如:

     # Your Arduino environment. ARD_HOME = /usr/share/arduino ARD_BIN = $(ARD_HOME)/hardware/tools/avr/bin # Monitor Baudrate MON_SPEED = 4800 # Board settings. BOARD = uno PORT = /dev/ttyACM0 PROGRAMMER = stk500v2 # Where to find header files and libraries. INC_DIRS = MY_LIB_DIRS = LIBS = LIB_DIRS = $(addprefix $(ARD_HOME)/libraries/, $(LIBS)) $(MY_LIB_DIRS) include ../Makefile.master 
  4. 使用make all编译和运行, make uploadmake monitor等。

确保您的Unix / Linux机器(或等效机器)上安装了picocom作为控制台串行监视器 。 在MAC-OS上 ,您可以通过相应地设置MON_CMD变量来使用屏幕


Makefile.master:

最初的Makefile.masterAlan Burlison编写,由Matthieu Weber修改,可以在这里找到。

我做了一些更改,以便它适合我的配置,特别是我添加了以下代码行:

 ### DEBUG Compilation ### ifeq ($(DEBUG), 1) ARD_FLAGS += -DDEBUG_PROJ C_FLAGS += -g CXX_FLAGS += -g else ARD_FLAGS += -DNDEBUG_PROJ endif 

然后从Makefile.master中的默认C/CXX _FLAGS条目中删除-g选项。 这样, 符号信息不会添加到发布代码中,并且只有在使用DEBUG=1编译代码时,代码才会被屏蔽

 #ifdef DEBUG_PROJ /* debug code here */ #endif // or #ifndef NDEBUG_PROJ /* debug code here */ #endif 

找到进入二进制文件的方式,从而产生较小的版本可执行文件。

在这里你可以找到我自己Makefile.master版本:

 # # Copyright 2011 Alan Burlison, alan@bleaklow.com. All rights reserved. # Subsequently modified by Matthieu Weber, matthieu.weber@jyu.fi. # Subsequently modified by Patrick Trentin, patrick.trentin.88@gmail.com # Use is subject to license terms. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # 1. Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # # 2. Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY ALAN BURLISON "AS IS" AND ANY EXPRESS OR IMPLIED # WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO # EVENT SHALL ALAN BURLISON OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, # OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, # EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # Makefile for building Arduino projects outside of the Arduino environment # # This makefile should be included into a per-project Makefile of the following # form: # # ---------- # BOARD = mega # PORT = /dev/term/0 # INC_DIRS = ../common # LIB_DIRS = ../libraries/Task ../../libraries/VirtualWire # include ../../Makefile.master # ---------- # # Where: # BOARD : Arduino board type, from $(ARD_HOME)/hardware/boards.txt # PORT : USB port # INC_DIRS : List pf directories containing header files # LIB_DIRS : List of directories containing library source # # Before using this Makefile you can adjust the following macros to suit # your environment, either by editing this file directly or by defining them in # the Makefile that includes this one, in which case they will override the # definitions below: # ARD_REV : arduino software revision, eg 0017, 0018 # ARD_HOME : installation directory of the Arduino software. # ARD_BIN : location of compiler binaries # AVRDUDE : location of avrdude executable # AVRDUDE_CONF : location of avrdude configuration file # PROGRAMMER : avrdude programmer type # MON_TERM : terminal command for serial monitor # MON_CMD : serial monitor command # MON_SPEED : serial monitor speed # # Global configuration. ARD_REV ?= 100 ARD_HOME ?= /usr/local/arduino ARD_BIN ?= /usr/bin AVRDUDE ?= $(ARD_HOME)/hardware/tools/avrdude AVRDUDE_CONF ?= $(ARD_HOME)/hardware/tools/avrdude.conf MON_TERM ?= xterm MON_SPEED ?= 57600 MON_CMD ?= picocom PORT ?= $(HOME)/dev/arduino BOARD ?= atmega328 ### Nothing below here should require editing. ### # Check for the required definitions. ifndef BOARD $(error $$(BOARD) not defined) endif ifndef PORT $(error $$(PORT) not defined) endif # Version-specific settings ARD_BOARDS = $(ARD_HOME)/hardware/arduino/boards.txt ARD_SRC_DIR = $(ARD_HOME)/hardware/arduino/cores/arduino ARD_MAIN = $(ARD_SRC_DIR)/main.cpp # Standard macros. SKETCH = $(notdir $(CURDIR)) BUILD_DIR = build VPATH = $(LIB_DIRS) # Macros derived from boards.txt MCU := $(shell sed -n 's/$(BOARD)\.build\.mcu=\(.*\)/\1/p' < $(ARD_BOARDS)) F_CPU := $(shell sed -n 's/$(BOARD)\.build\.f_cpu=\(.*\)/\1/p' < $(ARD_BOARDS)) UPLOAD_SPEED := \ $(shell sed -n 's/$(BOARD)\.upload\.speed=\(.*\)/\1/p' < $(ARD_BOARDS)) PROGRAMMER := \ $(shell sed -n 's/$(BOARD)\.upload\.protocol=\(.*\)/\1/p' < $(ARD_BOARDS)) ARD_VAR := \ $(shell sed -n 's/$(BOARD)\.build\.variant=\(.*\)/\1/p' < $(ARD_BOARDS)) # More Version-specific settings ARD_VAR_DIR = $(ARD_HOME)/hardware/arduino/variants/$(ARD_VAR) # Build tools. CC = $(ARD_BIN)/avr-gcc CXX = $(ARD_BIN)/avr-g++ CXXFILT = $(ARD_BIN)/avr-c++filt OBJCOPY = $(ARD_BIN)/avr-objcopy OBJDUMP = $(ARD_BIN)/avr-objdump AR = $(ARD_BIN)/avr-ar SIZE = $(ARD_BIN)/avr-size NM = $(ARD_BIN)/avr-nm MKDIR = mkdir -p RM = rm -rf MV = mv -f LN = ln -f # Compiler flags. INC_FLAGS = \ $(addprefix -I,$(INC_DIRS)) $(addprefix -I,$(LIB_DIRS)) -I$(ARD_SRC_DIR) -I$(ARD_VAR_DIR) ARD_FLAGS = -mmcu=$(MCU) -DF_CPU=$(F_CPU) -DARDUINO=$(ARD_REV) C_CXX_FLAGS = \ -Wall -Wextra -Wundef -Wno-unused-parameter \ -fdiagnostics-show-option -Wa,-adhlns=$(BUILD_DIR)/$*.lst C_FLAGS = \ $(C_CXX_FLAGS) -std=gnu99 -Wstrict-prototypes -Wno-old-style-declaration CXX_FLAGS = $(C_CXX_FLAGS) ### DEBUG Compilation ### ifeq ($(DEBUG), 1) ARD_FLAGS += -DDEBUG_PROJ C_FLAGS += -g CXX_FLAGS += -g else ARD_FLAGS += -DNDEBUG_PROJ endif # Optimiser flags. # optimise for size, unsigned by default, pack data. # separate sections, drop unused ones, shorten branches, jumps. # don't inline, vectorise loops. no exceptions. # no os preamble, use function calls in prologues. # http://gcc.gnu.org/onlinedocs/gcc-4.3.5/gcc/ # http://www.tty1.net/blog/2008-04-29-avr-gcc-optimisations_en.html OPT_FLAGS = \ -Os -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums \ -ffunction-sections -fdata-sections -Wl,--gc-sections,--relax \ -fno-inline-small-functions -fno-tree-scev-cprop -fno-exceptions \ -ffreestanding -mcall-prologues # Build parameters. IMAGE = $(BUILD_DIR)/$(SKETCH) ARD_C_SRC = $(wildcard $(ARD_SRC_DIR)/*.c) ARD_CXX_SRC = $(wildcard $(ARD_SRC_DIR)/*.cpp) ARD_C_OBJ = $(patsubst %.c,%.o,$(notdir $(ARD_C_SRC))) ARD_CXX_OBJ = $(patsubst %.cpp,%.o,$(notdir $(ARD_CXX_SRC))) ARD_LIB = arduino ARD_AR = $(BUILD_DIR)/lib$(ARD_LIB).a ARD_AR_OBJ = $(ARD_AR)($(ARD_C_OBJ) $(ARD_CXX_OBJ)) ARD_LD_FLAG = -l$(ARD_LIB) # Workaround for http://gcc.gnu.org/bugzilla/show_bug.cgi?id=34734 $(ARD_AR)(Tone.o) : CXX_FLAGS += -w # Sketch libraries. LIB_C_SRC = $(foreach ld,$(LIB_DIRS),$(wildcard $(ld)/*.c)) LIB_CXX_SRC = $(foreach ld,$(LIB_DIRS),$(wildcard $(ld)/*.cpp)) LIB_SRC = $(LIB_C_SRC) $(LIB_CXX_SRC) ifneq "$(strip $(LIB_C_SRC) $(LIB_CXX_SRC))" "" LIB_C_OBJ = $(patsubst %.c,%.o,$(notdir $(LIB_C_SRC))) LIB_CXX_OBJ = $(patsubst %.cpp,%.o,$(notdir $(LIB_CXX_SRC))) LIB_LIB = library LIB_AR = $(BUILD_DIR)/lib$(LIB_LIB).a LIB_AR_OBJ = $(LIB_AR)($(LIB_C_OBJ) $(LIB_CXX_OBJ)) LIB_LD_FLAG = -l$(LIB_LIB) endif # Sketch PDE source. SKT_PDE_SRC = $(wildcard *.pde *.ino) ifneq "$(strip $(SKT_PDE_SRC))" "" SKT_PDE_OBJ = $(BUILD_DIR)/$(SKETCH)_pde.o endif # C and C++ source. SKT_C_SRC = $(wildcard *.c) SKT_CXX_SRC = $(wildcard *.cpp) ifneq "$(strip $(SKT_C_SRC) $(SKT_CXX_SRC))" "" SKT_C_OBJ = $(patsubst %.c,%.o,$(SKT_C_SRC)) SKT_CXX_OBJ = $(patsubst %.cpp,%.o,$(SKT_CXX_SRC)) SKT_LIB = sketch SKT_AR = $(BUILD_DIR)/lib$(SKT_LIB).a SKT_AR_OBJ = $(SKT_AR)/($(SKT_C_OBJ) $(SKT_CXX_OBJ)) SKT_LD_FLAG = -l$(SKT_LIB) endif # Definitions. define run-cc @ $(CC) $(ARD_FLAGS) $(INC_FLAGS) -M -MT '$@($%)' -MF $@_$*.dep $< $(CC) -c $(C_FLAGS) $(OPT_FLAGS) $(ARD_FLAGS) $(INC_FLAGS) \ $< -o $(BUILD_DIR)/$% @ $(AR) rc $@ $(BUILD_DIR)/$% @ $(RM) $(BUILD_DIR)/$% @ $(CXXFILT) < $(BUILD_DIR)/$*.lst > $(BUILD_DIR)/$*.lst.tmp @ $(MV) $(BUILD_DIR)/$*.lst.tmp $(BUILD_DIR)/$*.lst endef define run-cxx @ $(CXX) $(ARD_FLAGS) $(INC_FLAGS) -M -MT '$@($%)' -MF $@_$*.dep $< $(CXX) -c $(CXX_FLAGS) $(OPT_FLAGS) $(ARD_FLAGS) $(INC_FLAGS) \ $< -o $(BUILD_DIR)/$% @ $(AR) rc $@ $(BUILD_DIR)/$% @ $(RM) $(BUILD_DIR)/$% @ $(CXXFILT) < $(BUILD_DIR)/$*.lst > $(BUILD_DIR)/$*.lst.tmp @ $(MV) $(BUILD_DIR)/$*.lst.tmp $(BUILD_DIR)/$*.lst endef # Rules. .PHONY : all clean upload monitor upload_monitor all : $(BUILD_DIR) $(IMAGE).hex clean : $(RM) $(BUILD_DIR) $(BUILD_DIR) : $(MKDIR) $@ $(SKT_PDE_OBJ) : $(SKT_PDE_SRC) if [ $(ARD_REV) -ge 100 ]; then \ echo '#include "Arduino.h"' > $(BUILD_DIR)/$(SKETCH)_pde.cpp; \ else \ echo '#include "WProgram.h"' > $(BUILD_DIR)/$(SKETCH)_pde.cpp; \ fi echo '#include "$(SKT_PDE_SRC)"' >> $(BUILD_DIR)/$(SKETCH)_pde.cpp $(LN) $(SKT_PDE_SRC) $(BUILD_DIR)/$(SKT_PDE_SRC) cd $(BUILD_DIR) && $(CXX) -c $(subst build/,,$(CXX_FLAGS)) \ $(OPT_FLAGS) $(ARD_FLAGS) -I.. \ $(patsubst -I..%,-I../..%,$(INC_FLAGS)) \ $(SKETCH)_pde.cpp -o $(@F) (%.o) : $(ARD_SRC_DIR)/%.c $(run-cc) (%.o) : $(ARD_SRC_DIR)/%.cpp $(run-cxx) (%.o) : %.c $(run-cc) (%.o) : %.cpp $(run-cxx) $(BUILD_DIR)/%.d : %.c $(run-cc-d) $(BUILD_DIR)/%.d : %.cpp $(run-cxx-d) $(IMAGE).hex : $(ARD_AR_OBJ) $(LIB_AR_OBJ) $(SKT_AR_OBJ) $(SKT_PDE_OBJ) $(CC) $(CXX_FLAGS) $(OPT_FLAGS) $(ARD_FLAGS) -L$(BUILD_DIR) \ $(SKT_PDE_OBJ) $(SKT_LD_FLAG) $(LIB_LD_FLAG) $(ARD_LD_FLAG) -lm \ -o $(IMAGE).elf $(OBJCOPY) -O ihex -j .eeprom --set-section-flags=.eeprom=alloc,load \ --no-change-warnings --change-section-lma .eeprom=0 $(IMAGE).elf \ $(IMAGE).eep $(OBJCOPY) -O ihex -R .eeprom $(IMAGE).elf $(IMAGE).hex $(OBJDUMP) -h -S $(IMAGE).elf | $(CXXFILT) -t > $(IMAGE).lst $(SIZE) $(IMAGE).hex upload : all - pkill -f '$(MON_CMD).*$(PORT)' - sleep 1 - stty -F $(PORT) hupcl - $(AVRDUDE) -V -C$(AVRDUDE_CONF) -p$(MCU) -c$(PROGRAMMER) -P$(PORT) \ -b$(UPLOAD_SPEED) -D -Uflash:w:$(IMAGE).hex:i monitor : LD_LIBRARY_PATH= LD_PRELOAD= \ $(MON_TERM) -title '$(BOARD) $(PORT)' \ -e '$(MON_CMD) -b $(MON_SPEED) $(PORT)' & upload_monitor : upload monitor -include $(wildcard $(BUILD_DIR)/*.dep)) # vim:ft=make 

使用示例:

给出如下的目录树

 Base_Dir ├── Library │  ├── Base │  │  ├── general STM8 sources and headers │  ├── STM8S_Discovery │  │  └── board specific sources and headers │  └── User │    └── optional user library sources and headers ├── Projects │  ├── Examples (to be filled) │  │  └── Basic_Project │  │  ├── config.h │  │  ├── example.ino │  │  └── Makefile --> should detect dependencies in ./*.c and ./*.h ... 

您可以将Makefile.master置于Projects ,然后假设:

  • 您只需要此项目的Library/BaseLibrary/User中的内容
  • 您需要在项目中使用LiquidCrystal Arduino Library

然后你将以下Makefile添加到Basic Project

 # Your Arduino environment. BASE_DIR = /path/to/Base_Dir # to edit ARD_HOME = /usr/share/arduino # to edit, maybe ARD_BIN = $(ARD_HOME)/hardware/tools/avr/bin # Monitor Baudrate MON_SPEED = 4800 # Board settings. BOARD = uno PORT = /dev/ttyACM0 PROGRAMMER = stk500v2 # Where to find header files and libraries. INC_DIRS = MY_LIB_DIRS= $(BASE_DIR)/Library/Base $(BASE_DIR)/Library/User LIBS= LiquidCrystal LIB_DIRS = $(addprefix $(ARD_HOME)/libraries/, $(LIBS)) $(MY_LIB_DIRS) include ../../Makefile.master 

请注意, common.h应该被自动检测,因为它位于. ,并且不需要将后者添加到INC_DIRS


最后注意:上次我测试了这个配置时,我使用的是版本1.0.5Arduino源代码,它运行得很完美。

您可以在此处找到GNU make的自动依赖关系生成方法的讨论以及示例实现。 你没有说你正在使用GNU make所以我只是假设。

我不知道这对你是否足够; 从您的陈述中不清楚您的要求。

首先,感谢所有人的快速和实质性的支持! 我应该早点问过……

现在回到我的(不再是)问题。 我现在明白我实际上问了两个不同的问题:

  • 依赖于标题 – >由Patrick或MadScientist的提议解决
  • 通过分析main.o中的调用或main.c中#includes来检测所需的库…?

我理解第二部分在内部难以实现……! 但Patrick的Makefile使手动配置非常方便。 所以这对我来说没问题:-)

[添加时间片……]好的,经过深思熟虑,以下工作/有意义吗?

  1. 从Makefile中调用例如gawk或python脚本,以隔离项目目录中* .c* .h的所有#included标头
  2. 对于每个包含的xyz.h,检查一个Lib目录中是否存在相应的make_xyz (将成为Lib的一部分)
  3. 如果是,请在主Makefile中包含用于编译的makefile

那是否有意义……? 再次感谢,祝你有个美好的一天,无论你身在何处!

此致,Georg Icking-Konert

再次感谢您的支持!

根据上面的建议,我写了一个小python脚本,它使用gcc(或实际上是sdcc)依赖生成器。 下面的脚本使用gcc扫描#included标头的所有项目.c文件。 然后在项目和库文件夹中搜索相应的头文件。 如果存在相应的.c文件(与标题相同的路径和名称),则将其添加到Makefile中。 重复此过程,直到找不到更多新标头。

结果是一个Makefile,它只构建项目.c文件中#included模块的模块 – 就像在Arduino IDE中一样。 它可能不是优雅但工作:-)

脚本中的第95-106行是编译器和项目特定的,必须相应地进行调整。 玩得开心,再次感谢!

 #!/usr/bin/python ''' automatically create a Makefile with dependencies from all .c files in a starting directory ''' # required modules import sys import os import platform import shlex from subprocess import Popen, PIPE # set OS specific OS = platform.system() if OS == 'Windows': MAKE = 'mingw32-make.exe' else: MAKE = 'make' ################## # helper functions ################## ######### def getchar(): """ python equivalent of getchar() """ ch = 0 if OS == 'Windows': import msvcrt as m ch = m.getch() sys.stdio.flush() sys.stderr.flush() else: import sys, tty, termios fd = sys.stdin.fileno() old_settings = termios.tcgetattr(fd) tty.setraw(sys.stdin.fileno()) ch = sys.stdin.read(1) return ch # end getchar() ######### def listFiles( start='.', pattern='.c' ): """ return set of matching files in project folder incl. subfolders """ result = set() for root, dirs, files in os.walk(start): for file in files: if file.endswith(pattern): #print(os.path.join(root, file)) result.add(os.path.join(root, file)) return result # end listFiles() ######### def listSubdirs( start='.' ): """ return set of subdirectories in given folder """ result = set() for root, dirs, files in os.walk(start): for dir in dirs: #print(os.path.join(root, dir)) result.add(os.path.join(root, dir)) return result # end listFiles() ######### def get_exitcode_stdout_stderr(cmd): """ execute the external command and get its exitcode, stdout and stderr. """ args = shlex.split(cmd) proc = Popen(args, stdout=PIPE, stderr=PIPE) out, err = proc.communicate() exitcode = proc.returncode return exitcode, out, err ################## # main program ################## # set compile search paths ROOT_DIR = '../../../' TOOL_DIR = ROOT_DIR + 'Tools/' LIB_ROOT = ROOT_DIR + 'Library/' PRJ_ROOT = '.' OBJDIR = 'output' TARGET = 'main.ihx' # set command for creating dependencies and set search paths CC = 'sdcc ' CFLAGS = '-mstm8 --std-sdcc99 --std-c99 ' LFLAGS = '-mstm8 -lstm8 --out-fmt-ihx ' DEPEND = '-MM ' INCLUDE = '-I. ' for dir in listSubdirs(PRJ_ROOT): INCLUDE += '-I' + dir + ' ' for dir in listSubdirs(LIB_ROOT): INCLUDE += '-I' + dir + ' ' # get set of .c files in project folder incl. subdirectories source_todo = listFiles(PRJ_ROOT,".c") source_done = set() header_done = set() object_done = set() # print message sys.stdout.write('start Makefile creation ... ') sys.stdout.flush() # generate generic Makefile header Makefile = open('Makefile', 'wb') Makefile.write('OBJDIR = '+OBJDIR+'\n') Makefile.write('TARGET = '+TARGET+'\n\n') Makefile.write('.PHONY: clean all default objects\n\n') Makefile.write('.PRECIOUS: $(TARGET)\n\n') Makefile.write('default: $(OBJDIR) $(OBJDIR)/$(TARGET)\n\n') Makefile.write('all: default\n\n') Makefile.write('# create output folder\n') Makefile.write('$(OBJDIR):\n') Makefile.write(' mkdir -p $(OBJDIR)\n') Makefile.write(' rm -fr -- -p\n\n') # iteratively add project sources to Makefile while (len(source_todo) > 0): # get next pending source and mark as done source = source_todo.pop() source_done.add(source) # convert Windows path to POSIX for Makefile if OS == 'Windows': source = source.replace('\\','/') # use compiler generate dependency list cmd = CC+DEPEND+CFLAGS+INCLUDE+source #print cmd exitcode, out, err = get_exitcode_stdout_stderr(cmd) if (exitcode != 0): print 'error: ' + err getchar() exit() # append .c file with dependency and compile instruction to Makefile Makefile.write('$(OBJDIR)/'+out) #print(out) Makefile.write('\t'+CC+CFLAGS+INCLUDE+'-c $< -o $@\n\n') # extract file list including object[0], source[1] and headers[2..N] out = out.replace(':', '') out = out.replace('\\', '') out = out.replace('\n', '') out = out.split() #print out # for all files returned by compiler... for next in out: # append object files for linker if next.endswith('.rel'): object_done.add(next) # if corresponding source to header exists, add to pending sources if next.endswith('.h'): if next not in header_done: # not yet in list header_done.add(next) # add to treated headers if (os.path.isfile(next[:-1]+'c')): # if corresponding .c exists, add to todo list source_todo.add(next[:-1]+'c') # link project object files Makefile.write('$(OBJDIR)/$(TARGET): ') for next in object_done: Makefile.write('$(OBJDIR)/'+next+' ') Makefile.write('\n') Makefile.write('\t'+CC+LFLAGS) for next in object_done: Makefile.write('$(OBJDIR)/'+next+' ') Makefile.write(' -o $@\n') # close Makefile.dep Makefile.close() print('done\n') sys.stdout.write('press any key to exit') getchar() exit() # END OF MODULE