How to write a Makefile

本文介绍了Makefile的基础知识,包括Makefile的组成部分如宏定义、目标、依赖等,并提供了实际的例子来展示如何使用Makefile进行软件项目的构建管理和自动化。

How to write a Makefile

Introduction

Make is one of the original Unix tools for Software Engineering. By S.I. Feldman of AT&T Bell Labs circa 1975. But there are public domain versions (eg. GNU) and versions for other systems (eg. Vax/VMS).

Related tools are the language compilers (cc, f77, lex, yacc, etc.) and shell programming tools (eg. awk, sed, cp, rm, etc.). You need to know how to use these.

Important adjuncts are lint (source code checking for obvious errors) ctags (locate functions, etc. in source code) and mkdepend. These are nice, and good programmers use them.

Important, and related tools, are the software revision systems SCCS (Source Code Control System) and RCS (Revision Control System -- the recommended choice)

The idea is to automate and optimize the construction of programs/files -- ie. to leave enough foot prints so that others can follow.

Makefile Naming

make is going to look for a file called Makefile, if not found then a file called makefile. Use the first (so the name stands out in listings).

You can get away without any Makefile (but shouldn't)! Make has default rules it knows about.

Makefile Components

  • Comments

    Comments are any text beginning with the pound (#) sign. A comment can start anywhere on a line and continue until the end of the line. For example:

    # $Id: slides,v 1.2 1992/02/14 21:00:58 reggers Exp $
  • Macros

    Make has a simple macro definition and substitution mechanism. Macros are defined in a Makefile as = pairs. For example:

    MACROS=  -me
    PSROFF= groff -Tps
    DITROFF= groff -Tdvi
    CFLAGS= -O -systype bsd43
    There are lots of default macros -- you should honor the existing naming conventions. To find out what rules/macros make is using type:
    % make -p
    NOTE: That your environment variables are exported into the make as macros. They will override the defaults.

    You can set macros on the make command line:

    % make "CFLAGS= -O" "LDFLAGS=-s" printenv
    cc -O printenv.c -s -o printenv
  • Targets

    You make a particular target (eg. make all), in none specified then the first target found:

    paper.dvi: $(SRCS)
    $(DITROFF) $(MACROS) $(SRCS) >paper.dvi
    NOTE: The the line beginning with $(DITROFF) begins with TAB not spaces.
    The target is made if any of the dependent files have changed. The dependent files in this case are represented by the $(SRCS) statement.

     

  • Continuation of Lines

    Use a back slash (/). This is important for long macros and/or rules.

     

  • Conventional Macros

    There are lots of default macros (type "make -p" to print out the defaults). Most are pretty obvious from the rules in which they are used:

    AR = ar
    GFLAGS =
    GET = get
    ASFLAGS =
    MAS = mas
    AS = as
    FC = f77
    CFLAGS =
    CC = cc
    LDFLAGS =
    LD = ld
    LFLAGS =
    LEX = lex
    YFLAGS =
    YACC = yacc
    LOADLIBS =
    MAKE = make
    MAKEARGS = 'SHELL=/bin/sh'
    SHELL = /bin/sh
    MAKEFLAGS = b
  • Special Macros

    Before issuing any command in a target rule set there are certain special macros predefined.

     

    1. $@ is the name of the file to be made.
    2. $? is the names of the changed dependents.

    So, for example, we could use a rule

    printenv: printenv.c
    $(CC) $(CFLAGS) $? $(LDFLAGS) -o $@
    alternatively:
    printenv: printenv.c
    $(CC) $(CFLAGS) $@.c $(LDFLAGS) -o $@
    There are two more special macros used in implicit rules. They are:
    1. $< the name of the related file that caused the action.
    2. $* the prefix shared by target and dependent files.

     

  • Makefile Target Rules

    The general syntax of a Makefile Target Rule is

        target [target...] : [dependent ....]
    [ command ...]
    Items in brackets are optional, ellipsis means one or more. Note the tab to preface each command is required.

    The semantics is pretty simple. When you say "make target" make finds the target rule that applies and, if any of the dependents are newer than the target, make executes the com- mands one at a time (after macro substitution). If any dependents have to be made, that happens first (so you have a recursion).

    A make will terminate if any command returns a failure sta- tus. That's why you see rules like:

    clean:
    -rm *.o *~ core paper
    Make ignores the returned status on command lines that begin with a dash. eg. who cares if there is no core file?

    Make will echo the commands, after macro substition to show you what's happening as it happens. Sometimes you might want to turn that off. For example:

    install:
    @echo You must be root to install
  • Example Target Rules

    For example, to manage sources stored within RCS (sometimes you'll need to "check out" a source file):

    SRCS=x.c y.c z.c

    $(SRCS):
    co $@
    To manage sources stored within SCCS (sometimes you'll need to "get" a source file):
    $(SRCS):
    sccs get $@
    Alternativley, to manage sources stored within SCCS or RCS let's generalize with a macro that we can set as required.
    SRCS=x.c y.c z.c
    # GET= sccs get
    GET= co

    $(SRCS):
    $(GET) $@
    For example, to construct a library of object files
    lib.a: x.o y.o z.o
    ar rvu lib.a x.o y.o z.o
    ranlib lib.a
    Alternatively, to be a bit more fancy you could use:
    OBJ=x.o y.o z.o
    AR=ar

    lib.a: $(OBJ)
    $(AR) rvu $@ $(OBJ)
    ranlib $@
    Since AR is a default macro already assigned to "ar" you can get away without defining it (but shouldn't).

    If you get used to using macros you'll be able to make a few rules that you can use over and over again.
    For example, to construct a library in some other directory

    INC=../misc
    OTHERS=../misc/lib.a

    $(OTHERS):
    cd $(INC); make lib.a
    Beware:, the following will not work (but you'd think it should)
    INC=../misc
    OTHERS=../misc/lib.a

    $(OTHERS):
    cd $(INC)
    make lib.a
    Each command in the target rule is executed in a separate shell. This makes for some interesting constructs and long continuation lines.

    To generate a tags file

    SRCS=x.c y.c z.c
    CTAGS=ctags -x >tags

    tags: $(SRCS)
    ${CTAGS} $(SRCS)
    On large projects a tags file, that lists all functions and their invocations is a handy tool.
    To generate a listing of likely bugs in your problems
    lint:
    lint $(CFLAGS) $(SRCS)
    Lint is a really good tool for finding those obvious bugs that slip into programs -- eg. type classes, bad argu- ment list, etc.

     

  • Some Basic Make Rule

    People have come to expect certain targets in Makefiles. You should always browse first, but it's reasonable to expect that the targets all (or just make), install, and clean will be found.

     

    1. make all -- should compile everything so that you can do local testing before installing things.
    2. make install -- should install things in the right places. But watch out that things are installed in the right place for your system.
    3. make clean -- should clean things up. Get rid of the executables, any temporary files, object files, etc.

    You may encounter other common targets, some have been already mentioned (tags and lint).

     

  • An Example Makefile for printenv

     

    # make the printenv command
    #
    OWNER=bin
    GROUP=bin
    CTAGS= ctags -x >tags
    CFLAGS= -O
    LDFLAGS= -s
    CC=cc
    GET=co
    SRCS=printenv.c
    OBJS=printenv.o
    SHAR=shar
    MANDIR=/usr/man/manl/printenv.l
    BINDIR=/usr/local/bin
    DEPEND= makedepend $(CFLAGS)
    all: printenv

    # To get things out of the revision control system
    $(SRCS):
    $(GET) $@
    # To make an object from source
    $(CC) $(CFLAGS) -c $*.c

    # To make an executable

    printenv: $(OBJS)
    $(CC) $(LDFLAGS) -o $@ $(OBJS)

    # To install things in the right place
    install: printenv printenv.man
    $(INSTALL) -c -o $(OWNER) -g $(GROUP) -m 755 printenv $(BINDIR)
    $(INSTALL) -c -o $(OWNER) -g $(GROUP) -m 644 printenv.man $(MANDIR)

    # where are functions/procedures?
    tags: $(SRCS)
    $(CTAGS) $(SRCS)

    # what have I done wrong?
    lint: $(SRCS)
    lint $(CFLAGS) $(SRCS)

    # what are the source dependencies
    depend: $(SRCS)
    $(DEPEND) $(SRCS)

    # to make a shar distribution
    shar: clean
    $(SHAR) README Makefile printenv.man $(SRCS) >shar

    # clean out the dross
    clean:
    -rm printenv *~ *.o *.bak core tags shar

    # DO NOT DELETE THIS LINE -- make depend depends on it.
    printenv.o: /usr/include/stdio.h
  • Makefile Implicit Rules

    Consider the rule we used for printenv

    printenv: printenv.c
    $(CC) $(CFLAGS) printenv.c $(LDFLAGS) -o printenv
    We generalized a bit to get
    printenv: printenv.c
    $(CC) $(CFLAGS) $@.c $(LDFLAGS) -o $@
    The command is one that ought to work in all cases where we build an executable x out of the source code x.c This can be stated as an implicit rule:
    .c:
    $(CC) $(CFLAGS) $@.c $(LDFLAGS) -o $@
    This Implicit rule says how to make x out of x.c -- run cc on x.c and call the output x. The rule is implicit because no particular target is mentioned. It can be used in all cases.

    Another common implicit rule is for the construction of .o (object) files out of .c (source files).

    .o.c:
    $(CC) $(CFLAGS) -c $<
    alternatively
    .o.c:
    $(CC) $(CFLAGS) -c $*.c
  • Make Dependencies

    It's pretty common to have source code that uses include files. For example:

    % cat program.c

    #include
    #include "defs.h"
    #include "glob.h"
    etc....
    main(argc,argv)
    etc...
    The implicit rule only covers part of the source code depen- dency (it only knows that program.o depends on program.c). The usual method for handling this is to list the dependen- cies separately;
            etc...
    $(CC) $(CFLAGS) -c $*.c
    etc...
    program.o: program.c defs.h glob.h
    Usually an implicit rule and a separate list of dependencies is all you need. And it ought to be easy enough to figure out what the dependencies are.

    However, there are a number of nice tools around that will automatically generate dependency lists for you. For example (trivial):

    DEPEND= makedepend $(CFLAGS)
    etc...
    # what are the source dependencies

    depend: $(SRCS)
    $(DEPEND) $(SRCS)

    etc....
    # DO NOT DELETE THIS LINE -- ....

    printenv.o: /usr/include/stdio.h
    These tools (mkdepend, mkmkf, etc.) are very common these days and aren't too difficult to use or understand. They're just shell scripts that run cpp (or cc -M, or etc.) to find out what all the include dependencies are. They then just tack the dependency list onto the end of the Makefile.

     

Based on Make and Makefiles by

Reg Quinton
Computing and Communications Services
The University of Western Ontario
London, Ontario N6A 5B7
Canada

 


Press here to return to the General Unix Software Menu.

源码直接下载地址: https://pan.quark.cn/s/32a64cc0d812 LKH 算法在中文中的表述为 LKH 算法,它是一种用于处理 TSP(旅行商问题)与 VRP(车辆配送问题)等组合优化挑战的启发式算法,并且该算法是 Lin-Kernighan 启发式方法的进一步发展。该算法的开发与执行过程具有相当的挑战性,然而,它被认为是获取对称旅行商问题最优或接近最优答案的最有效途径之一。LKH 算法的升级版本通过运用灵敏度分析来引导并约束搜索过程,从而使得该算法能够在可接受的时间内为大规模问题找出最优解。通过计算实验的验证,证明该方法具备高效性,能够在不足一秒的时间范围内寻得典型100座城市问题的最优方案,而对于典型的1000座城市问题,也能在不到一分钟的时间框内找到最优解。旅行商问题(TSP)是组合优化领域中研究最为深入的课题之一,该问题可以通过成本矩阵 C 的特性来进行分类。此问题可划分为对称性情形与非对称性情形,同时依据三角不等式的成立与否,可进一步区分为度量性情形与非度量性情形。TSP 的显著地位源于其广泛的实际应用,其中许多应用看似与旅行路径无直接关联。众多现实场景能够以 TSP 的形式来模拟,例如计算机内部布线、车辆路径规划、晶体结构分析、机器人导航控制、印刷电路板打孔定位以及时间表的制定等。TSP 作为一种典型的组合优化课题,其研究对于解决该学科范畴内的其他课题往往具有指导意义。事实上,组合优化领域的诸多突破均可追溯至对 TSP 问题的深入探索。计算方法中广为人知的 branch and bound 技术最初便是在 TSP 的研究背景下被引入的。攻克 TSP 所面临的智力难题亦起到了推动作用,该问题的表述看似简单,却极难求解。当考虑到可能...
内容概要:本文研究了基于深度Q网络(DQN)与非正交多址接入(NOMA)技术相结合的无人机上行链路干扰管理方法,并提供了完整的Python代码实现。通过构建DQN强化学习模型,动态优化无人机在复杂无线环境中的资源分配策略,有效缓解多用户接入带来的同频干扰问题,提升上行链路的通信效率与系统容量。研究充分融合了DQN在决策优化方面的自主学习能力与NOMA在频谱效率提升上的技术优势,重点探讨了在高动态、强干扰的无人机通信场景下,如何实现高效的干扰协调与功率控制。仿真实验验证了该方法在不同用户密度和信道条件下的鲁棒性与优越性,显著降低了误码率并提高了系统吞吐量。; 适合人群:具备一定Python编程能力和机器学习基础,熟悉强化学习或无线通信领域的研究生、科研人员及相关领域工程师。; 使用场景及目标:①研究无人机通信系统中的动态干扰管理和资源调度问题;②学习DQN在通信网络优化中的建模、训练与部署流程;③复现并改进基于NOMA的多用户接入干扰抑制方案,推动智能通信算法的实际应用; 阅读建议:此资源结合理论分析与代码实践,建议读者在掌握强化学习基本原理和无线通信基础知识的前提下,结合所提供的Python代码进行仿真实验,深入理解DQN与NOMA融合机制,并尝试调整网络结构、奖励函数及通信参数以进一步优化系统性能。
代码下载链接: https://pan.quark.cn/s/a4b39357ea24 “东北大学——C语言大作业——养老社区源码.zip”是由东北大学学子独立完成的关于C语言编程的项目。该压缩文件内含了构建养老社区管理系统的源代码,其设立目的或许在于教学实践或评估编程水平,属于课程作业的范畴。 “C语言大作业,因众多学子所需而再度上传的版本”揭示了这一资源的高需求度,表明其在学生群体中具备较高的参考意义。鉴于需求旺盛,上传者选择重新发布,暗示该项目可能兼具实用价值或挑战性,超越了一般学习材料的范畴,从而成为学生间交流学习与借鉴的重要对象。 “C语言”、“社区系统”、“东北大学”构成了此项目的核心标签。“C语言”明确了编程工具,作为计算机科学的基础,它在系统级编程及嵌入式开发领域应用广泛。“社区系统”暗示项目内容可能涵盖用户管理、数据管理、交互机制等,构建一个模拟现实社区管理的信息系统。“东北大学”则标示了该作业的学术背景,暗示了其遵循的教育理念和可能的教学水准。 【源码剖析】:在“养老社区源码”中,我们能够预见以下核心知识点: 1. **基础数据结构**:C语言中的结构体(struct)可能被应用于定义养老社区中的各类实体,例如老人档案、员工档案、房间档案等,以此促进数据的有序组织与高效管理。 2. **文件处理**:为保障社区数据的持久化存储,源代码中或许包含了文件读写功能,运用C语言的fopen、fwrite、fread等函数执行操作。 3. **链表与数组**:在社区管理系统的开发中,动态存储和检索数据是常见需求,链表与数组作为常用数据结构,可用于存储和查询用户数据。 4. **函数构建**:C语言的函数将承担实现各项功能的作...
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值