謝皇廷
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Versions and GitHub Sync Note Insights Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       owned this note    owned this note      
    Published Linked with GitHub
    Subscribed
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    Subscribe
    How to use makefile === ## 基礎觀念 ### 介紹:makefile 是什麼? makefile 會告訴 make 該如何建立應用程式 ### 不用 make 該如何編譯程式? 我們有 main.c 2.c 3.c * step1:產生所有.c檔的目標檔 `# gcc -c main.c`:產生 main.o 目標檔,而#為 root 模式 ```clike= //main.c #include "a.h" ``` `# gcc -c 2.c`:產生2.o目標檔 ```clike= //2.c #include "a.h" #include "b.h" ``` `# gcc -c 3.c`:產生3.o目標檔 ```clike= //3.c #include "b.h" #include "c.h" ``` * step2:製作執行檔 `# gcc -o main main.o 2.o 3.o`:其中的 main 為我們想建立的執行檔名稱 * step3:執行執行檔 `# ./main` ### 使用 make 該如何編譯? * step1 `# vi makefile`:先用vim編寫文件(以下只列出大概格式,並非實際例子) ``` main: main.o 2.o 3.o gcc -o main main.o 2.o 3.o main.o: main.c gcc -c main.c 2.o: 2.c a.h gcc -c 2.c 3.o: 3.c b.h gcc -c 3.c ``` * step2 `# make`:進行編譯 * step3 `# ./main`:執行執行檔 ### makefile 第1步:了解核心觀念 核心觀念: ``` target : 目標檔1 目標檔2 <tab>gcc -o 欲建立執行檔的名稱 目標檔1 目標檔2 ``` 舉例: ``` myapp : main.o 2.o 3.o gcc -o myapp 2.o 3.o ``` ### makefile 第2步:知道相依性項目 透過以下指令也可看到當前自己的程式碼的相依性項目,要把所有.c檔羅列出來 `$ gcc -MM main.o 2.c 3.c ` ``` main.o : main.c a.h 2.o : 2.c a.h b.h 3.o : 3.c b.h c.h ``` 所有相依性項目可以寫成 ``` myapp : main.o 2.o 3.o main.o : main.c a.h 2.o : 2.c a.h b.h 3.o : 3.c b.h c.h ``` ***TODO:可以補充樹狀圖*** 這時候如果改變 b.h,那麼2.o 3.o 會受到影響,因此myapp也會受到影響 ![](https://i.imgur.com/auucIge.png) (樹狀圖工具)graphviz `sudo apt-get install graphviz` [GraphViz for discrete math students](http://graphs.grevian.org/graph/4993093174558720) ``` digraph{ "main.c"->"main.o" "a.h"->"main.o" "2.c"->"2.o" "b.h"->"2.o" "a.h"->"2.o" "b.h"->"3.o" "3.c"->"3.o" "c.h"->"3.o" "main.o"->"myapp" "2.o"->"myapp" "3.o"->"myapp" } ``` ### makefile 第3步:當target很多怎麼辦 舉例: ``` all : myapp myapp.1 ``` 產生 myapp 執行檔,myapp1 執行手冊 ### makefile 第4步:實作 makefile,但檔名使用Makefile1 ##### step1:以下為`$vi Makefile1`頁面 ``` myapp : main.o 2.o 3.o gcc -o myapp main.o 2.o 3.o main.o : main.c a.h gcc -c main.c 2.o : 2.c a.h b.h gcc -c 2.c 3.o : 3.c b.h c.h gcc -c 3.c ``` ##### step2:執行 make 命令,但是程式碼不存在,跳出錯誤訊息 `$ make -f Makefile1` 技巧:-f參數後面是接 filename,代表告訴 make 他心中的 makefile 是 Makefile1 跳出 make:***No rule to make target 'main.c' need by main.o ##### step3:利用 touch 產生空白標頭檔 `$ touch a.h` `$ touch b.h` `$ touch c.h` ##### step4:編譯程式 main.c 2.c 3.c ```clike= /*main.c*/ #include <stdio.h> #include "a.h" extern void function_two(); extern void function_three(); int main() { function_two(); function_three(); } ``` ```clike= /*2.c*/ #include "a.h" #include "b.h" void function_two(){ } ``` ```clike= /*3.c*/ #include "b.h" #include "c.h" void function_three(){ } ``` ##### step5:再使用 make 編譯程式 `$ make -f Makefile1` ``` gcc -c main.c gcc -c 2.c gcc -c 3.c gcc -o myapp main.o 2.o 3.o ``` ##### step6:如果 b.h 有修改過 影響了 2.o 3.o myapp `$ touch b.h` `$ make -f Makefile1` ``` gcc -c 2.c gcc -c 3.c gcc -o myapp main.o 2.o 3.o ``` ### makefile 第5步:技巧分享 `$ make -j4`:同時執行4個命令 `$ make -k`:編譯發生錯誤仍然執行 `$ make -n`:只印出將進行工作,而不去真正編譯 在 makefile 內打#是註解 ## 進階觀念 ### makefile 進階第1步:了解變數目的 適用於不同編譯器 可以簡化makefile ### makefile 進階第2步:了解變數定義 MACRONAME=value 1.左邊為變數名稱,右邊為變數內容 2.變數名稱最好都用大寫 3.=兩側不能出現':',兩側可以有空格 4.value 變成空白代表變數被清空 5.變數名稱左邊不可以有 tab ### makefile 進階第3步:變數存取 `$(MACRONAME) or ${MACRONAME} ` ### makefile 進階第4步:以變數簡化makefile original `$ vi makefile` ``` main : main.o haha.o sin.o cos.o gcc -o main main.o haha.o sin.o cos.o -lm clean : rm -f main main.o haha.o sin.o cos.o ``` now:define the LIBS and OBJS `$ vi makefile` ``` LIBS = -lm OBJS = main.o haha.o sin.o cos.o main : $(OBJS) gcc -o main $(OBJS) $(LIBS) clean : rm -f main $(OBJS) ``` ### makefile 進階第5步:嘗試定義變數以環境變數 CFLAGS 為例 法1:在命令列定義 `$ make clean main "CFLAGS=-Wall"` 法2:在 makefile 內定義 `$ vi makefile` ``` LIBS = -lm OBJS = main.o haha.o sin_value.o cos_value.o CFLAGS = -Wall main: ${OBJS} gcc -o main ${OBJS} ${LIBS} clean: rm -f main ${OBJS} ``` <重點>環境變數取用規則: make 指令列後面加上的環境變數為優先; makefile 裡面指定的環境變數第二; shell 原本具有的環境變數第三。 ### makefile 進階第6步:實作一個含有變數的 makefile 目標項目 all 只會產生 myapp。執行 make 建立目標項目 myapp。 `$ vi Makefile2` ``` all: myapp # Which compiler CC = gcc # Where are include files kept INCLUDE = . # Options for development (-g Produce debugging information) CFLAGS = -g -Wall -ansi # Options for release # CFLAGS = -O -Wall -ansi myapp: main.o 2.o 3.o $(CC) -o myapp main.o 2.o 3.o main.o: main.c a.h $(CC) -I$(INCLUDE) $(CFLAGS) -c main.c 2.o: 2.c a.h b.h $(CC) -I$(INCLUDE) $(CFLAGS) -c 2.c 3.o: 3.c b.h c.h $(CC) -I$(INCLUDE) $(CFLAGS) -c 3.c ``` 執行: `$ rm *.o myapp` 應該是移除所有.o檔,還有 myapp 執行檔 `$ make -f Makefile2` 用-f參數去收到 makefile2 gcc -I. -g -Wall -ansi -c main.c gcc -I. -g -Wall -ansi -c 2.c gcc -I. -g -Wall -ansi -c 3.c gcc -o myapp main.o 2.o 3.o 注意:以下3個被變數所取代 ``` all: myapp INCLUDE = . CFLAGS = -g -Wall -ansi ``` ### makefile 進階第7步:make 常用的語法 #### 一些符號定義 * := 變數的值決定於它在 Makefile 中的位置,而不是整個 Makefile 展開後最終的值 * ?= 若變數未定義,則替它指定新的值。否則,採用原有的值 如︰FOO ?= bar 若 FOO 未定義,則 FOO = bar;若 FOO 已定義,則 FOO 的值維持不變。 * += 此時 CFLAGS 的值就變成 -Wall -g -O2 ``` CFLAGS = -Wall -g CFLAGS += -O2 ``` #### 自動化變數 * $? 代表需要重建(被修改)的相依性項目。 * $@ 目前的目標項目檔名。 * $< 第一個必要條件的檔名。 * $* 工作目標的主檔名。 * $^ 所有必要條件的檔名, 並以空格隔開這些檔名。 (這份清單已經拿掉重複的檔名) 例題:以 $@ 代表目前的目標 (target) 項目。 [guest@test guest]# vi makefile ``` LIBS = -lm OBJS = main.o haha.o sin_value.o cos_value.o CFLAGS = -Wall main: ${OBJS} gcc -o $@ ${OBJS} ${LIBS} #在此 $@ 即 main,也就是執行檔名稱 clean: rm -f main ${OBJS} ``` #### 特殊字元 makefile 中兩個特別字元,可以加在要執行的命令之前: * -:make 會忽略命令的錯誤。 ``` 如果希望產生一個目錄,但希望忽略錯誤,可能是因為該目錄已經存在。 -mkdir /usr/local/repository 如果希望清除目標檔案,但希望忽略錯誤,可能是因為該檔案不存在。 clean: -rm main.o 2.o 3.o ``` * @ :不要顯示執行的指令,因執行 make 指令後會在終端機印出正在執行的指令 ``` 判斷式 if 起始為符號 @,讓 make 在執行該法則時,停止印出標準輸出的文字。 install: myapp @if [ -d $(INSTDIR) ]; \ then \ ...;\ fi ``` #### 萬用字元 makefile 中所用的萬用字元是 % ,代表所有可能的字串,前後可以接指定的字串來表示某些固定樣式的字串。例如 %.c 表示結尾是 .c 的所有字串。因此我們改寫 makefile 如下 ```clike= CC = gcc OBJS = a.o b.o c.o all: test %.o: %.c $(CC) -c -o $@ $< test: $(OBJS) $(CC) -o test &^ ``` 試著重建上述語法:未必正確 ```clike= all:test a.o:a.c gcc -c -o main ? b.o:b.c gcc -c -o main ? c.o:c.c gcc -c -o main ? test:a.o b.o c.o gcc -o test ? ``` ## 深入觀念:多重目標項目(target) ### 在 makefile 建立多個目標項目,用 make 去指定要哪個目標項目 例題:沿上例,加入 clean 目標項目,以移除不想要的目的檔(object)。 建立編譯的規則 [guest@test guest]# vi makefile main: main.o haha.o sin_value.o cos_value.o gcc -o main main.o haha.o sin_value.o cos_value.o -lm clean: rm -f main main.o haha.o sin_value.o cos_value.o #%* clean 冒號之後是空白。目標項目永遠會被認為過期,所以它的法則永遠會被執行。*) 測試標的 clean: [guest@test guest]# make clean rm -f main main.o haha.o sin_value.o cos_value.o 先清除目標檔再編譯程式 main : make clean main 會先幫你清空.o檔跟執行檔,再幫你編譯一次 [guest@test guest]# make clean main rm -f main main.o haha.o sin_value.o cos_value.o cc -c -o main.o main.c cc -c -o haha.o haha.c cc -c -o sin_value.o sin_value.c cc -c -o cos_value.o cos_value.c gcc -o main main.o haha.o sin_value.o cos_value.o -lm ### 尚未理解 沿上例,再加入 install 目標項目,將完成的應用程式安裝到不同的目錄。 ``` makefile 新的版本 Makefile3: all: myapp # Which compiler CC = gcc # Where to install INSTDIR = /usr/local/bin # Where are include files kept INCLUDE = . # Options for development CFLAGS = -g -Wall -ansi # Options for release # CFLAGS = -O -Wall -ansi myapp: main.o 2.o 3.o $(CC) -o myapp main.o 2.o 3.o main.o: main.c a.h $(CC) -I$(INCLUDE) $(CFLAGS) -c main.c 2.o: 2.c a.h b.h $(CC) -I$(INCLUDE) $(CFLAGS) -c 2.c 3.o: 3.c b.h c.h $(CC) -I$(INCLUDE) $(CFLAGS) -c 3.c clean: -rm main.o 2.o 3.o install: myapp # 每個命令都會啟動一個新的 shell,所以必須加上反斜線(\),讓所有 script 命令在同一行,且在同一個 shell 中執行。 @if [ -d $(INSTDIR) ]; \ then \ cp myapp $(INSTDIR);\ chmod a+x $(INSTDIR)/myapp;\ chmod og-w $(INSTDIR)/myapp;\ echo "Installed in $(INSTDIR)";\ else \ echo "Sorry, $(INSTDIR) does not exist";\ fi &&:前個命令成功,才執行下個命令 @if [ -d $(INSTDIR) ]; \ then \ cp myapp $(INSTDIR) &&\ chmod a+x $(INSTDIR)/myapp && \ chmod og-w $(INSTDIR/myapp && \ echo "Installed in $(INSTDIR)" ;\ else \ echo "Sorry, $(INSTDIR) does not exist" ; false ; \ fi 執行 $ rm *.o myapp $ make -f Makefile3 gcc -I. -g -Wall -ansi -c main.c gcc -I. -g -Wall -ansi -c 2.c gcc -I. -g -Wall -ansi -c 3.c gcc -o myapp main.o 2.o 3.o $ make -f Makefile3 make: Nothing to be done for 'all'. $ rm myapp $ make -f Makefile3 install gcc -o myapp main.o 2.o 3.o Installed in /usr/local/bin $ make -f Makefile3 clean rm main.o 2.o 3.o $ ``` ``` 請解釋如下 makefile 片段,假設變數 INSTDIR=mydir。 install: myapp @if [ -d $(INSTDIR) ]; \ then \ cp myapp $(INSTDIR);\ chmod a+x $(INSTDIR)/myapp;\ chmod og-w $(INSTDIR)/myapp;\ echo "Installed in $(INSTDIR)";\ else \ echo "Sorry, $(INSTDIR) does not exist";\ fi Sol. 1.目標項目 install 的相依性項目有 myapp; 2. @ 表示不會在標準輸出上,顯示要執行的命令; 3.如果 mydir 是一個目錄,則執行複製 myapp 到目錄 mydir、改變 mydir/myapp 的屬性,所有使用者加上可執行屬性、其他使用者及同群組使用者刪除可寫屬性;螢幕輸出 Installed in mydir; 4.其他(即 mydir 不是一個目錄,則螢幕輸出 Sorry, mydir does not exist; 5.因為每個命令都會啟動一個新的 shell,所以必須加上反斜線(\),讓所有 script 命令在同一行,且在同一個 shell 中執行。 如下 makefile ,&& \ 代表意義為何? chmod a+x $(INSTDIR)/myapp && \ chmod og-w $(INSTDIR/myapp && \ Sol. && 表示前個命令成功,才執行下個命令,\ 讓 script 命令在同一行,且在同一個 shell 中執行。 ``` ## 技巧 ==解說==:使用 .PHONY: clean,會把 clean 當作一個 fake 項目,不認為這是要產生的檔案,因此 makefile 不會去檢查目錄中是否有 clean 檔案,提升 make 的效率 ```cmake= .PHONY: clean clean: rm *.o ``` ==解說==: * @ 不要顯示執行的指令 因執行 make 指令後會在終端機印出正在執行的指令 * '-' 表示即使該行指令出錯,也不會中斷執行 而 make 只要遇到任何錯誤就會中斷執行。但像是在進行 clean 時,也許根本沒有任何檔案可以 clean,因而 rm 會傳回錯誤值,因而導致 make 中斷執行。我們可以利用 - 來關閉錯誤中斷功能,讓 make 不會因而中斷。 ```cmake= .PHONY: clean clean: @echo "Clean..." -rm *.o ``` reference [make 命令和 makefile](https://dywang.csie.cyut.edu.tw/dywang/linuxProgram/node49.html) [Makefile 語法和示範](https://hackmd.io/s/SySTMXPvl#) [2016q3 Homework01 (raytracing)](https://hackmd.io/s/HyHhgcv6#graphviz) [Makefile 語法簡介](http://tetralet.luna.com.tw/?op=ViewArticle&articleId=185)

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully