iOS Link Map File計(jì)算探索

1、Link Map File 是啥

我們編寫的OC代碼需要經(jīng)過預(yù)編譯->編譯->匯編->鏈接(靜態(tài)鏈接)纬霞,最終生成一個(gè)可執(zhí)行文件冻辩。匯編階段完成后,每個(gè)類都會(huì)生成一個(gè)對(duì)應(yīng)的.o文件(可重定位的目標(biāo)文件)触幼,在鏈接階段(靜態(tài))硼瓣,會(huì)把所有的.o文件鏈接到一起,生成一個(gè)可執(zhí)行文件置谦。Link Map File就是這樣一個(gè)記錄鏈接相關(guān)信息的純文本文件堂鲤,里面記錄了可執(zhí)行文件的路徑、CPU架構(gòu)媒峡、目標(biāo)文件瘟栖、符號(hào)等信息。

2谅阿、探索 Link Map File 的意義

探索 Link Map File 可以幫助我們更好的理解鏈接過程半哟、理解內(nèi)存分段及分區(qū)酬滤、分析可執(zhí)行文件中哪個(gè)類或庫占用比較大,進(jìn)行安裝包瘦身寓涨。

蘋果對(duì)上傳到App Store上的app大小也有嚴(yán)格的規(guī)定 Apple 規(guī)定最大構(gòu)建版本文件大小

1599047151702-c23d1104-6c40-487d-a031-a20241d1cb94.png

翻譯:
執(zhí)行文件大小是指執(zhí)行文件的__TEXT部分

  • 當(dāng)iOS最低版本小于7.0盯串,最多為80MB;
  • 想iOS系統(tǒng)版本位于 7.0~8.0之間時(shí)戒良,每個(gè)分區(qū)是60MB(并不是指32位+64位最多為120MB体捏,當(dāng)32位分區(qū)占用50MB,64位分區(qū)占用61MB蔬墩,總111MB也不行译打,因?yàn)?4位分區(qū)超出了);
  • 當(dāng)iOS大于等于9.0拇颅,總限制500MB奏司;

對(duì)App來說,可以用Link Map File 來分析各個(gè)文件占用大小樟插,有針對(duì)性進(jìn)行優(yōu)化韵洋。
對(duì)SDK來說,也可以用Link Map File 來計(jì)算自身大小黄锤,控制好大小這個(gè)度搪缨。一般大App對(duì)集成二方和三方這樣的SDK大小是有很嚴(yán)格的限制。

3鸵熟、如何生成 Link Map File

Xcode 默認(rèn)情況下是不會(huì)去生成 Link Map File副编,需要開發(fā)者自己手動(dòng)去打開生成配置開關(guān),如下圖流强,Target -> Build Setting -> Linking -> Write Link Map File 為 YES

1599045570555-e26f5c29-099f-49eb-959e-151df97fa7fa.png

存放 Link Map File 文件路徑如下圖

1599045684267-815539f6-c00f-4b44-9839-c14f08948452.png

默認(rèn)存放路徑是

$(TARGET_TEMP_DIR)/$(PRODUCT_NAME)-LinkMap-$(CURRENT_VARIANT)-$(CURRENT_ARCH).txt

最終翻譯成實(shí)體文件路徑如下

/Users/chao/Library/Developer/Xcode/DerivedData/Demo-fvacayrprqdluqdcpzegtlzjjoqp/Build/Intermediates.noindex/Demo.build/Debug-iphonesimulator/Demo.build/Demo-LinkMap-normal-x86_64.txt

我們也可更改 Link Map File 文件存放位置痹届,這個(gè)要看自己需要了

4、Link Map File 組成

1. Path:生成可執(zhí)行文件的路徑

# Path: /Users/chao/Library/Developer/Xcode/DerivedData/Demo-fvacayrprqdluqdcpzegtlzjjoqp/Build/Products/Debug-iphonesimulator/Demo.app/Demo

2. Arch:架構(gòu)類型

# Arch: x86_64

3. Object files:列舉了可執(zhí)行文件里所有的obj以及tbd打月。

# Object files:
[  0] linker synthesized
[  1] /Users/chao/Library/Developer/Xcode/DerivedData/Demo-fvacayrprqdluqdcpzegtlzjjoqp/Build/Intermediates.noindex/Demo.build/Debug-iphonesimulator/Demo.build/Demo.app-Simulated.xcent
[  2] /Users/chao/Library/Developer/Xcode/DerivedData/Demo-fvacayrprqdluqdcpzegtlzjjoqp/Build/Intermediates.noindex/Demo.build/Debug-iphonesimulator/Demo.build/Objects-normal/x86_64/ViewController.o
[  3] /Users/chao/Library/Developer/Xcode/DerivedData/Demo-fvacayrprqdluqdcpzegtlzjjoqp/Build/Intermediates.noindex/Demo.build/Debug-iphonesimulator/Demo.build/Objects-normal/x86_64/AFSecurityPolicy.o
[  4] /Users/chao/Library/Developer/Xcode/DerivedData/Demo-fvacayrprqdluqdcpzegtlzjjoqp/Build/Intermediates.noindex/Demo.build/Debug-iphonesimulator/Demo.build/Objects-normal/x86_64/AppDelegate.o
...

4. Sections:主要用展示代碼和數(shù)據(jù)在內(nèi)存中的分布队腐,涉及到Mach-O可執(zhí)行文件內(nèi)存分布相關(guān),這里不做過多解釋

# Sections:
# Address   Size        Segment Section
0x100004350 0x0002308D  __TEXT  __text
0x1000273DE 0x000002A0  __TEXT  __stubs
0x100027680 0x00000470  __TEXT  __stub_helper
......
0x100030000 0x00000008  __DATA  __nl_symbol_ptr
0x100030008 0x000000F0  __DATA  __got
0x1000300F8 0x00000380  __DATA  __la_symbol_ptr
......

5. Symbols:記錄符號(hào)相關(guān)信息奏篙,也是我們統(tǒng)計(jì)的關(guān)鍵

# Symbols:
# Address   Size        File  Name
0x100004350 0x00000040  [  2] -[ViewController viewDidLoad]
0x100004390 0x000000E0  [  2] -[ViewController touchesBegan:withEvent:]
0x100004470 0x00000050  [  2] ___41-[ViewController touchesBegan:withEvent:]_block_invoke
0x1000044C0 0x00000080  [  2] ___41-[ViewController touchesBegan:withEvent:]_block_invoke_2
0x100004540 0x00000074  [  2] ___41-[ViewController touchesBegan:withEvent:]_block_invoke_3
0x1000045C0 0x00000330  [  3] +[AFSecurityPolicy certificatesInBundle:]
0x1000048F0 0x00000080  [  3] +[AFSecurityPolicy defaultPolicy]

根據(jù)Sections的起始地址柴淘,可以將Symbols分為Sections個(gè)數(shù)的組
Symbols包含的信息有:

  • Address:符號(hào)起始地址
  • Size:所占內(nèi)存大小(16進(jìn)制)秘通。
  • File:該Name所在的文件編號(hào)为严,也就是Object files部分的中括號(hào)的數(shù)字,例如-[ViewController viewDidLoad]對(duì)應(yīng)的文件編號(hào)為[ 2]肺稀,根據(jù)Object files部分可以看到所屬的文件為:ViewController.o梗脾。這樣可以計(jì)算某個(gè).o文件所占內(nèi)存的大小。只需要把Symbols中編號(hào)為.o對(duì)應(yīng)編號(hào)符號(hào)累加統(tǒng)計(jì)即可盹靴。
  • Name:符號(hào)的名稱炸茧。

6. Dead Stripped Symbols:鏈接器認(rèn)為無用的符號(hào)瑞妇,統(tǒng)計(jì)大小的時(shí)候不統(tǒng)計(jì)它下面的符號(hào)

# Dead Stripped Symbols:
#           Size        File  Name
<<dead>>    0x00000018  [  2] CIE
<<dead>>    0x00000015  [  3] literal string: supportsSecureCoding
<<dead>>    0x0000000F  [  3] literal string: SSLPinningMode
<<dead>>    0x00000011  [  3] literal string: pinnedPublicKeys

5、Link Map File 信息統(tǒng)計(jì)

接下里正式進(jìn)入本文主題梭冠,使用 Link Map 文件統(tǒng)計(jì)各個(gè)文件的大小辕狰。

說到統(tǒng)計(jì),真的是心酸的不行控漠,開始寫了一個(gè)shell的統(tǒng)計(jì)腳本蔓倍,結(jié)果發(fā)現(xiàn)計(jì)算速度太慢(主要是沒有面向?qū)ο蟮乃枷耄矝]有我想要的數(shù)據(jù)結(jié)構(gòu)盐捷,例如map)右冻。為了加快統(tǒng)計(jì)的速度险绘,我用OC寫了個(gè)linkmap計(jì)算程序,速度是快了,但是想到運(yùn)行OC畢竟要裝Xcode才能運(yùn)行蹂随,哎误续!還能怎樣这刷,接著探索唄百匆。這個(gè)時(shí)候我想到一門語言,Python习霹! 面向?qū)ο髮懩_本朵耕,正是我需要的,于是扒資料扒教程得去學(xué)習(xí)Python淋叶,學(xué)完出山阎曹,寫了一個(gè)linkmap.sh程序,計(jì)算速度果然大幅度增加煞檩,比較滿意处嫌。這樣就滿足了嗎?不形娇!生命不息,折騰不止筹误!這讓我想起了之前無意間看到C++之父說過的一句話桐早,大概的意思是“我為全球變暖做出的貢獻(xiàn)就是讓C ++運(yùn)行更高效”,這句話激勵(lì)了我去重新去認(rèn)識(shí)C ++這門語言厨剪,于是我買了李明杰的C++課程哄酝,一番學(xué)習(xí)下來收獲頗豐,于是用C ++重寫了計(jì)算程序祷膳。

5.1 人狠話不多陶衅,先上耗時(shí)對(duì)比

首先我用工程編譯出一個(gè) Link Map File,有 1.7w 多行符號(hào)直晨,也就是說腳本要解析計(jì)算1.7w多行文本信息

程序 耗時(shí)
shell腳本 548.183s
Python腳本 0.231s
C++程序 0.107s

5.2 慢速的shell

初期寫了個(gè)shell統(tǒng)計(jì)腳本搀军,但是shell腳本沒有map這樣的數(shù)據(jù)結(jié)構(gòu)膨俐,這讓人很痛苦,因?yàn)橐粋€(gè)文件可能對(duì)應(yīng)很多符號(hào)罩句,解析到某一個(gè)符號(hào)之后焚刺,首先要找出文件,再找出文件之前統(tǒng)計(jì)的大小门烂,加上該符號(hào)大小乳愉,以此類推。設(shè)計(jì)上我用了兩個(gè)數(shù)組屯远,一個(gè)裝文件編號(hào)蔓姚,一個(gè)裝文件對(duì)應(yīng)累加大小,兩個(gè)數(shù)組通過位置對(duì)應(yīng)起來慨丐。解析某個(gè)文件對(duì)應(yīng)的符號(hào)需要遍歷兩個(gè)數(shù)組坡脐,所以慢的要死。腳本代碼如下

#!/bin/sh

if [[ $# < 1 ]]; then
  echo "腳本正確使用方式:./linkmap.sh <link-map-file-pat> <keyword>"
  echo "示例:./linkmap.sh ./linkmap.txt"
  echo "示例:./linkmap.sh ./linkmap.txt ATAuthSDK"
  exit 0
fi

declare -a file_number_arr
declare -a file_name_arr
declare -a file_size_arr

reach_files=0
reach_sections=0
reach_symbols=0

number=`cat $1 | wc -l`
progress_view=''
current_number=0
progress=0

while read line
do
  #進(jìn)度條相關(guān)
  ((current_number++))
  if [[ $((current_number * 100 / number)) -ge $((progress + 1)) ]]; then
    progress_view+='#'
    progress=$((current_number * 100 / number))
  fi
  printf "[%-100s] %d%% \r" "$progress_view" "$progress";


  if [[ -n `echo $line | grep '^# Object files:'` ]]; then
    reach_files=1
  elif [[ -n `echo $line | grep '^# Sections:'` ]]; then
    reach_sections=1
  elif [[ -n `echo $line | grep '^# Symbols:'` ]]; then
    reach_symbols=1
  elif [[ -n `echo $line | grep '^# Dead Stripped Symbols:'` ]]; then
    break;
  fi

  if [[ $reach_files -ne    0 && $reach_sections -eq 0 && $reach_symbols -eq 0 ]]; then
    if [[ -n `echo $line | grep ']'` ]]; then
      count=${#file_number_arr[@]}
      file_number_arr[count]=`echo $line | egrep -o '\[([0-9 ]*)\]'`
      file_name_arr[count]=${line#*] }
      file_size_arr[count]=0
    fi
  elif [[ $reach_files -ne  0 && $reach_sections -ne 0 && $reach_symbols -ne 0 ]]; then
    file_number=`echo $line | egrep -o '\[([0-9 ]*)\]'`
    file_size=`echo ${line%]*} | cut -d ' ' -f 2`
    echo "第 $current_number 行 大小為:$file_size" >> log.txt
    if [[ -n "${file_number}" && -n "${file_size}" ]]; then
      idx=-1
      for (( i = 0; i < ${#file_number_arr[@]}; i++ )); do
        if [[ "${file_number_arr[$i]}" = "${file_number}" ]]; then
          idx=$i
        fi
      done

      if [[ $idx -ge 0 ]]; then
        file_size_arr[$idx]=$(( ${file_size_arr[$idx]} + ((file_size)) ))
      fi
    fi
  fi

  ((tag++))
done < "${1}"

total_size=0

printf "\n%-40s \t\t %-10s %-20s\n" "文件名" "文件編號(hào)" "文件大小"
for (( i = 0; i < ${#file_name_arr[@]}; i++ )); do
  if [[ -n "${2}" ]]; then
    if [[ -n `echo "${file_name_arr[i]}" | grep "${2}"` ]]; then
      printf "%-40s \t %-10s %-20s\n" "${file_name_arr[$i]##*/}" "${file_number_arr[$i]}" "${file_size_arr[$i]} byte"
      ((total_size+=${file_size_arr[$i]}))
    fi
  else
    printf "%-40s \t %-10s %-20s\n" "${file_name_arr[$i]##*/}" "${file_number_arr[$i]}" "${file_size_arr[$i]} byte"
    ((total_size+=${file_size_arr[$i]}))
  fi
done

echo "\n總大小 ${total_size} byte ≈ $((total_size / 1024)) kb"

5.3 高鐵速度的Python

對(duì)比上面的shell腳本咖气,Python腳本快了不止一倍兩倍挨措,上面有數(shù)據(jù)統(tǒng)計(jì)。因?yàn)镻ython是面向?qū)ο蟮木幊陶Z言崩溪,所以代碼上更清晰明朗浅役,看起來也很舒服,同時(shí)還增加了排序功能伶唯,可以根據(jù)需要觉既,選擇文件名排序、文件大小升序排序乳幸、文件大小降序排序中的一種瞪讼,只需要傳入不同的參數(shù)即可實(shí)現(xiàn)。

#!/usr/bin/python
# -*- coding: UTF-8 -*-

import sys,os
from enum import Enum, unique

@unique
class LMPStep(Enum):
    Initial     = 0
    Object      = 1
    Section     = 2
    Symbols     = 3
    Finish      = 4

class LMPOrder(Enum):
    FileName    = 0 #按文件名輸出顯示
    SizeAsc     = 1 #按文件大小升序排序
    SizeDesc    = 2 #按文件大小降序排序


class LMPModel(object):
    "符號(hào)信息記錄類"
    def __init__(self, number, path, name, size=0):
        self.number = number
        self.path = path
        self.name = name
        self.size = size
    def add(self, size):
        self.size += size
    def __str__(self):
        return 'number=%s,name=%s,size=%d' \
        % (self.number, self.name, self.size)

def build_symbol_model(line):
    "根據(jù) # Object files: 中的行構(gòu)建 LCSymbolModel 對(duì)象"
    if '[' not in line or ']' not in line:
        return None
    snumber = line[line.index('['):line.index(']') + 1]
    spath = line[line.index(']') + 1:]
    sname = spath.split('/')[-1].strip()
    model = LMPModel(snumber, spath, sname)
    return model

def parse_linkmap_file(filepath):
    if os.path.exists(filepath) == False:
        print '請(qǐng)檢查 %s 文件是否存在' % filepath
        return None
    try:
        linkmap_file = open(filepath, mode='r')
    except Exception as e:
        print '%s 文件讀取失敗' % filepath
        return None
    dict = {}
    step = LMPStep.Initial
    line = linkmap_file.readline()
    while line:
        line = line.strip()
        if len(line) > 0:
            if line.startswith('#'):
                if line.startswith('# Object files:'):
                    step = LMPStep.Object
                elif line.startswith('# Sections:'):
                    step = LMPStep.Section
                elif line.startswith('# Symbols:'):
                    step = LMPStep.Symbols
                elif line.startswith('# Dead Stripped Symbols:'):
                    step = LMPStep.Finish
            else:
                if step == LMPStep.Object:
                    model = build_symbol_model(line)
                    if model and len(model.number) > 0:
                        dict[model.number] = model
                    else:
                        print 'Object 解析異常:%s' % line
                elif step == LMPStep.Symbols:
                    snumber = line[line.index('['):line.index(']') + 1]
                    array = line.split('\t')
                    if len(snumber) <= 0 or snumber not in dict or len(array) < 3:
                        print 'Symbols 解析異常:%s' % line
                    else:
                        size = int(array[1], 16)
                        dict[snumber].add(size)
                    pass
                elif step == LMPStep.Finish:
                    break
        line = linkmap_file.readline()
    linkmap_file.close()
    return dict

def show(dict, keyword = None, order = LMPOrder.FileName):
    total_size = 0
    if dict:
        if order == LMPOrder.SizeAsc.value:
            models = sorted(dict.values(), key = lambda m: m.size)
        elif order == LMPOrder.SizeDesc.value:
            models = sorted(dict.values(), key = lambda m: m.size, reverse=True)
        else:
            models = sorted(dict.values(), key = lambda m: m.name)
        for model in models:
            if keyword:
                if keyword in model.name:
                    print '%-40s %-6d byte' % (model.name, model.size)
                    total_size += model.size
            else:
                print '%-40s %-6d byte' % (model.name, model.size)
                total_size += model.size
    print '總大小 %d byte ≈ %.2f kb' % (total_size, total_size / 1024.0)

if __name__ == '__main__':
    if len(sys.argv) < 2:
        print '腳本正確使用方式:./linkmap.py <link-map-file-pat> <keyword> <order(0:文件名 1:文件大小升序 2:文件大小降序)>'
        print '示例:./linkmap.py ./linkmap.txt'
        print '示例:./linkmap.py ./linkmap.txt ATAuthSDK'
        print '示例:./linkmap.py ./linkmap.txt ATAuthSDK 0'
        sys.exit(0)
    filepath = sys.argv[1]
    keyword = None
    order = LMPOrder.FileName
    if len(sys.argv) > 2:
        keyword = sys.argv[2]
    if len(sys.argv) > 3:
        order = int(sys.argv[3])
    dict = parse_linkmap_file(filepath)
    if dict:
        show(dict, keyword, order)

5.4 火箭速度的C++

#include <string>
#include <unordered_map>
#include <fstream>
#include <vector>
#include <iomanip>
using namespace std;

class Model {
    string number;
    string path;
    string name;
    int size;
public:
    Model();
    Model(string number, string path);
    void add(int size);
    string getNumber();
    string getPath();
    string getName();
    int getSize();
    friend ostream& operator<<(ostream &, const Model&);
};

void parse_linkmap_file(string&, unordered_map<string, Model>&);
void show(unordered_map<string, Model>&, string&, int);

int main(int argc, const char * argv[]) {
    //1. 參數(shù)檢查
    if (argc < 2) {
        cout << "命令正確使用方式:./linkmap <link-map-file-path> <keyword> <order(0:文件名 1:文件大小升序 2:文件大小降序)>" << endl;
        cout << "示例:./linkmap ./linkmap.txt" << endl;
        cout << "示例:./linkmap ./linkmap.txt ATAuthSDK" << endl;
        cout << "示例:./linkmap ./linkmap.txt ATAuthSDK 0" << endl;
        return 0;
    }
    string filepath = argv[1];
    string keyword{};
    if (argc > 2) {
        keyword = argv[2];
    }
    int order = 0;
    if (argc > 3) {
        order = *(argv[3]) - '0';
    }
    
    //2. 解析
    unordered_map<string, Model> map = {};
    parse_linkmap_file(filepath, map);
    
    //3. 展示
    show(map, keyword, order);
    return 0;
}


Model::Model() {}
Model::Model(string number, string path) : number(number), path(path), size(0) {
    string::size_type idx = path.rfind('/');
    if (idx == string::npos) {
        name = path;
    } else {
        name = path.substr(idx + 1);
    }
}
string Model::getNumber() { return number; }
string Model::getPath() { return path; }
string Model::getName() { return name; }
int Model::getSize() { return size; }

void Model::add(int size) {
    this->size += size;
}

bool starts_with(const string& s1, const string& s2) {
    return s1.size() >= s2.size() && s1.compare(0, s2.size(), s2) == 0;
}
vector<string> split(const string& str, const string& delim) {
    vector<string> tokens;
    size_t prev = 0, pos = 0;
    do {
        pos = str.find(delim, prev);
        if (pos == string::npos) {
            pos = str.size();
        }
        string token = str.substr(prev, pos - prev);
        if (!token.empty()) {
            tokens.push_back(token);
        }
        prev = pos + delim.size();
    } while (pos < str.size() && prev < str.size());
    return tokens;
}

string find_number(const string& line) {
    string::size_type ns = line.find('[');
    string::size_type ne = line.find(']');
    if (ns == string::npos || ne == string::npos || ns > ne) {
        return string();
    }
    return line.substr(ns, ne - ns + 1);
}
Model build_model(const string& line) {
    string snumber = find_number(line);
    if (snumber.size() == 0) {
        return Model();
    }
    string spath = line.substr(line.find(']')+2);
    return Model(snumber, spath);
}

void parse_linkmap_file(string& filepath, unordered_map<string, Model>& map) {
    ifstream fin(filepath);
    if (!fin) {
        cout << "請(qǐng)檢查 " << filepath << " 是否存在" << endl;
        return;
    }
    enum { Initial, Object, Section, Symbols, Finish } step;
    step = Initial;
    string line;
    while (getline(fin, line)) {
        if (line.size() == 0) continue;
        if (starts_with(line, "#")) {
            if (starts_with(line, "# Object files:")) {
                step = Object;
            }
            else if (starts_with(line, "# Sections:")) {
                step = Section;
            }
            else if (starts_with(line, "# Symbols:")) {
                step = Symbols;
            }
            else if (starts_with(line, "# Dead Stripped Symbols:")) {
                step = Finish;
            }
        }
        else {
            if (step == Object) {
                Model m = build_model(line);
                if (m.getNumber().size() > 0) {
                    map[m.getNumber()] = m;
                } else {
                    cout << "Object 解析異常:" << line << endl;
                }
            }
            else if (step == Symbols) {
                string snumber = find_number(line);
                vector<string> array = split(line, "\t");
                if (snumber.size() == 0 || map.count(snumber) == 0 || array.size() < 3) {
                    cout << "Symbols 解析異常:" << line << endl;
                }
                else {
                    int size = (int)strtol(array[1].c_str(), nullptr, 16);
                    unordered_map<string, Model>::iterator it = map.find(snumber);
                    it->second.add(size);
                }
            }
            else if (step == Finish) {
                break;
            }
        }
    }
    fin.close();
}

void show(unordered_map<string, Model>& map, string& keyword, int order) {
    typedef pair<string, Model> Pair;

    int total_size = 0;
    vector<Pair> pairs(map.begin(), map.end());
    sort(pairs.begin(), pairs.end(), [=](Pair& lhs, Pair& rhs) {
        if (order == 1) {
            return lhs.second.getSize() < rhs.second.getSize();
        }
        else if (order == 2) {
            return lhs.second.getSize() > rhs.second.getSize();
        }
        else {
            return lhs.second.getName() < rhs.second.getName();
        }
    });
    
    for (int i = 0; i != pairs.size(); ++i) {
        Model model = pairs[i].second;
        if (keyword.size() > 0) {
            if (model.getName().find(keyword) != string::npos) {
                cout << model << endl;
                total_size += model.getSize();
            }
        }
        else {
            cout << model << endl;
            total_size += model.getSize();
        }
    }
    
    cout << "總大小 " << total_size << " byte ≈ " << total_size / 1024.0 << " kb" << endl;
}

ostream& operator<<(ostream& os, const Model& m) {
    os << left << setw(8) <<  m.number << left << setw(40) << m.name << m.size;
    return os;
}

C++ 代碼寫出來了粹断,接下來就是編譯成可執(zhí)行文件了

5.4.1 Mac OS 系統(tǒng)下

當(dāng)然是使用Xcode自帶的Clang編譯器了符欠,雖然底層用的還是GCC,編譯指令如下

clang++ -std=c++11 -stdlib=libc++ linkmap.cpp -o linkmap
  • -std指定編譯的標(biāo)準(zhǔn)瓶埋,我這里選擇的是C++11這個(gè)標(biāo)準(zhǔn)希柿,因?yàn)槲业拇a是基于該標(biāo)準(zhǔn)開發(fā)的
  • -stdlib指定C++標(biāo)準(zhǔn)庫
  • -o后面指定編譯成可執(zhí)行文件的名字,不指定會(huì)默認(rèn)編譯成a.out(ps:其實(shí)這里的a.out是Unix系統(tǒng)很早版本的可執(zhí)行文件格式后綴养筒,感興趣可以查下相關(guān)資料)

這兩個(gè)編譯配置在Xcode里面也能找到

1599055244953-b809d6a6-7d97-4c73-8eab-b2c092249e21.png

現(xiàn)在已經(jīng)編譯出來了可執(zhí)行文件了(名字為 linkmap)曾撤,讓我們一起開心的使用,我的linkmap 可執(zhí)行文件放在 Documents 目錄下

# 1. cd 到 linkmap可執(zhí)行文件目錄下
cd ~/Documents

# 2. 使用 linkmap 計(jì)算程序
./linkmap LinkMap.txt

NO晕粪!這不是我們想要的挤悉,每次還要cd到可執(zhí)行文件對(duì)應(yīng)的目錄下,才能使用該指令巫湘,我在其他目錄下就不能使用了嗎装悲?我想要隨心所欲地用昏鹃,任何地方打開終端就可以用。這個(gè)時(shí)候我讓我想到了環(huán)境變量PATH衅斩,想到咱就來盆顾,沒有一絲猶豫

首先回顧下Mac系統(tǒng)默認(rèn)配置文件加載順序:/etc/profile /etc/paths ~/.bash_profile
~/.bash_login ~/.profile ~/.bashrc,其中/etc/profile/etc/paths是系統(tǒng)級(jí)別的畏梆,系統(tǒng)啟動(dòng)就會(huì)加載您宪,后面三個(gè)是當(dāng)前用戶級(jí)的環(huán)境變量。如果~/.bash_profile文件存在奠涌,則后面的兩個(gè)文件就會(huì)被忽略不讀了宪巨,如果~/.bash_profile文件不存在,才會(huì)以此類推讀取后面的文件溜畅。~/.bashrc沒有上述規(guī)則捏卓,它是bash shell打開的時(shí)候載入的。

如果讓我來選擇配置環(huán)境變量慈格,肯定是放在~/.bash_profile文件里面嘍怠晴,但是因?yàn)槲业碾娔X安裝了zsh,導(dǎo)致~/.bash_profile不會(huì)被執(zhí)行浴捆,而是執(zhí)行~/.zshrc蒜田,所以這里我會(huì)放到~/.zshrc里面去配置環(huán)境變量,當(dāng)然如果你安裝了zsh选泻,同時(shí)又在 ~/.zshrc 執(zhí)行了~/.bash_profilesource ~/.bash_profile)冲粤,那也可以配置在~/.bash_profile里面

接下來正式開始環(huán)境變量配置工作:
1、首先我們?cè)谟脩裟夸浵聞?chuàng)建一個(gè) mybin 目錄專門用來存放我們自己寫的程序指令

mkdir ~/mybin

2页眯、將我們編譯好的 linkmap 可執(zhí)行文件放到新建好的~/mybin目錄下
3梯捕、配置環(huán)境變量
將如下代碼加入到對(duì)應(yīng)的配置文件(ps:我的電腦因安裝了zsh,所以要在~/.zshrc文件里面配置窝撵,如果沒有安裝zsh傀顾,需要在~/.bash_profile文件里面配置)

PATH=$PATH:~/mybin
export PATH

最后執(zhí)行source ~/.zshrc或重啟終端即可,這樣我就可以在地方打開終端碌奉,隨用所欲使用linkmap指令啦短曾。

看下效果,我cd到不同目錄下去調(diào)用linkmap指令道批,都能識(shí)別到错英。效果如下入撒,舒服了隆豹!舒服了!

20211021163506.jpg

用起來方便茅逮、快捷璃赡、高效判哥!滿意!5锟肌塌计!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市侯谁,隨后出現(xiàn)的幾起案子锌仅,更是在濱河造成了極大的恐慌,老刑警劉巖墙贱,帶你破解...
    沈念sama閱讀 212,599評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件热芹,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡惨撇,警方通過查閱死者的電腦和手機(jī)伊脓,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,629評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來魁衙,“玉大人报腔,你說我怎么就攤上這事∑实恚” “怎么了纯蛾?”我有些...
    開封第一講書人閱讀 158,084評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)祷蝌。 經(jīng)常有香客問我茅撞,道長(zhǎng),這世上最難降的妖魔是什么巨朦? 我笑而不...
    開封第一講書人閱讀 56,708評(píng)論 1 284
  • 正文 為了忘掉前任米丘,我火速辦了婚禮,結(jié)果婚禮上糊啡,老公的妹妹穿的比我還像新娘拄查。我一直安慰自己,他們只是感情好棚蓄,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,813評(píng)論 6 386
  • 文/花漫 我一把揭開白布堕扶。 她就那樣靜靜地躺著,像睡著了一般梭依。 火紅的嫁衣襯著肌膚如雪稍算。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,021評(píng)論 1 291
  • 那天役拴,我揣著相機(jī)與錄音糊探,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛科平,可吹牛的內(nèi)容都是我干的褥紫。 我是一名探鬼主播,決...
    沈念sama閱讀 39,120評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼瞪慧,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼髓考!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起弃酌,我...
    開封第一講書人閱讀 37,866評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤氨菇,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后妓湘,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體门驾,經(jīng)...
    沈念sama閱讀 44,308評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,633評(píng)論 2 327
  • 正文 我和宋清朗相戀三年多柑,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了奶是。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,768評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡竣灌,死狀恐怖聂沙,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情初嘹,我是刑警寧澤及汉,帶...
    沈念sama閱讀 34,461評(píng)論 4 333
  • 正文 年R本政府宣布,位于F島的核電站屯烦,受9級(jí)特大地震影響坷随,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜驻龟,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,094評(píng)論 3 317
  • 文/蒙蒙 一温眉、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧翁狐,春花似錦类溢、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,850評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至懈词,卻和暖如春蛇耀,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背坎弯。 一陣腳步聲響...
    開封第一講書人閱讀 32,082評(píng)論 1 267
  • 我被黑心中介騙來泰國(guó)打工纺涤, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留躁倒,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,571評(píng)論 2 362
  • 正文 我出身青樓洒琢,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親褐桌。 傳聞我的和親對(duì)象是個(gè)殘疾皇子衰抑,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,666評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容

  • 1. Link Map File 簡(jiǎn)介 Link Map File 直譯為 鏈接映射文件,在編譯階段荧嵌,每個(gè)類會(huì)生成...
    沉江小魚閱讀 692評(píng)論 0 1
  • Link Map File初識(shí) 我們編寫的源碼需要經(jīng)過編譯呛踊、鏈接,最終生成一個(gè)可執(zhí)行文件啦撮。在編譯階段谭网,每個(gè)類會(huì)生成...
    Mrshang110閱讀 19,908評(píng)論 6 54
  • Link Map File Link Map File 直譯為 鏈接映射文件,是 Xcode 生成可執(zhí)行文件時(shí)一起...
    頂級(jí)蝸牛閱讀 1,273評(píng)論 0 4
  • 什么是Link Map File Link Map File中文直譯為鏈接映射文件赃春,它是在Xcode生成可執(zhí)行文件...
    我不是掌柜閱讀 1,195評(píng)論 0 2
  • 前言 我們編寫的源碼需要經(jīng)過編譯愉择、鏈接,最終生成一個(gè)可執(zhí)行文件织中。在編譯階段锥涕,每個(gè)類會(huì)生成對(duì)應(yīng)的 .o 文件(目標(biāo)文...
    __Mr_Xie__閱讀 2,217評(píng)論 0 4