iT邦幫忙

2023 iThome 鐵人賽

DAY 13
0

接下來本篇要實做的範例內容會是實際更改原始碼,然後依據更改的內容來更新版本號,在不重新編譯主程式僅更新動態庫的情況下看看那一些情況可以成功執行,那一些情況需要重新編譯主程式。

CMake語法

project

project( [VERSION] .. LANGUAGES )

PROJECT-NAME : 專案名稱
VERSION .. : 定義主要、次要、補釘 版本
LANGUAGES : 使用的語言

定義出的變數
$(PROJECT_VERSION) : 定義出的[主要. 次要.補釘] 版本
$(PROJECT_VERSION_MAJOR) : 定義出的主要版本
$(PROJECT_VERSION_MINOR): 定義出的次要版本
$(PROJECT_VERSION_PATCH) : 定義出的補丁版本

set_target_properties

set_target_properties(<target_name> VERSION SOVERSION )

VERSION: 這是程式庫的「建構(build)」版本,和一般的軟體版本一樣採用 major.minor.patch 格式。

SOVERSION: 這指的是程式庫的「介面(API)」版本,也稱為SONAME,會影響連結器找尋適用版本的行為。

而上一篇有說過會影響介面版本的行為應該該增加major版本,minor和patch的更新並不會影響到介面,因此$ (SOVERSION)並不會變動,變動的是$(VERSION)的minor和patch 。

因此庫版本我會使用以下設定方式,實做版本和專案版本相同,介面版本和主要版本相同。

set_target_properties([target] PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION ${PROJECT_VERSION_MAJOR})

範例 1.0.0

$ git clone https://github.com/m11112089/2023_iT_CMake.git
$ cd ~/2023_iT_CMake/Day13

P.S. 在 Day13/snapshot 資料夾中有存放各個版本的快照

1. 編寫 MathFunctions/CMakeLists.txt

  • 設定專案名稱、版本、與使用語言
    project(cmake_totorial VERSION 1.0.0 LANGUAGES CXX)
    # 設定專案名稱為MathFunctions,版本為1.0.0,且此項目使用C++
    
  • 設定庫的 VERSION 和 SOVERSION
    set_target_properties(mysqrt PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION ${PROJECT_VERSION_MAJOR})
    # 設定庫的版本號,實做版本和專案版本相同,介面版本和主要版本相同
    

2. 進入/MathFunctions/build 資料夾編譯出動態庫文件

$ cd /MathFunctions/build
$ cmake ..
$ make

可以看到我們在MathFunctions/CMakeLists.txt中設定的庫導出路徑 ${CMAKE_CURRENT_SOURCE_DIR}/../lib 下,出現了和I7565H1/H2 SDK 的 lib 資料夾中類似的檔案。
內容是libmysqrt.so軟鍊結到libmysqrt.so.1、
libmysqrt.so.1軟鍊結到libmysqrt.so.1.0.0、
還有libmysqrt.so.1.0.0

kai@esoc:~/2023_iT_CMake/Day13/lib$ tree
.
├── libmysqrt.so -> libmysqrt.so.1
├── libmysqrt.so.1 -> libmysqrt.so.1.0.0
└── libmysqrt.so.1.0.0

3. 編寫 CMakeLists.txt

鍊結執行檔與動態庫

target_link_libraries(main mysqrt)
# 將mysqrt連結到執行檔main

可以看到,我們並不會指定鍊結的庫版本,而是透過軟鍊結去尋找。

4. 編譯主程式
回到 /build 資料夾中編譯主程式,將主程式和動態庫鍊結在一起

$ cmake ..
$ make

5. 使用ldd指令查看編譯出的執行檔的鍊結關係

$ ldd main

kai@esoc:~/2023_iT_CMake/Day13/build$ ldd main 
	linux-vdso.so.1 (0x00007ffd043f3000)
	libmysqrt.so.1 => /home/kai/2023_iT_CMake/Day13/lib/libmysqrt.so.1 (0x00007fa20ae72000) <----- 鍊結到的mysqrt⭐
	libstdc++.so.6 => /lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007fa20ac00000)
	libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007fa20ae2c000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fa20a800000)
	libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fa20ab19000)
	/lib64/ld-linux-x86-64.so.2 (0x00007fa20ae7f000)

可以看到main鍊結到libmysqrt.so.1,而libmysqrt.so.1實際上是使用軟鍊結去鍊結到libmysqrt.so.1.0.0,因此main實際上鍊結到的會是libmysqrt.so.1.0.0。

那為什麼需要出現軟鍊結呢?
因為set_target_properties會設定「介面」版本(SO_NAME)軟鍊結到最新的「建構」版本,而這樣做的原因就是為了實現動態庫版本更新時不需要重新編譯,可以直接鍊結到最新版動態庫。
6. 執行主程式

$ ./main 100

kai@esoc:~/2023_iT_CMake/Day13/build$ ./main 100
0Computing sqrt of 100 to be 50.5
1Computing sqrt of 100 to be 26.2401
2Computing sqrt of 100 to be 15.0255
3Computing sqrt of 100 to be 10.8404
4Computing sqrt of 100 to be 10.0326
The square root of 100 is 10.0326

範例 1.0.1

接下來這個範例會更改原始碼並更新版本號,而不需要讓主程式重新編譯。
1. 優化程式碼

此版本的更新內容是將 mysqrt.cpp 的迭代次數變成10次,因為迭代5次精度可能不夠,並新增一個靜態常數變數來儲存迭代次數,不會更改到介面,因此可以預期ABI二進制接口相同,不用重新編譯主程式。

//mysqrt 1.0.0
#include "mysqrt.h"
#include <iostream>
double sqrt(double x)
{
  if (x <= 0) return 0;
  double result = x;

  for (int i = 0; i < 5; ++i) {
    if (result <= 0) {
      result = 0.1;
    }
    double delta = x - (result * result);
    result = result + 0.5 * delta / result;
    std::cout << i << "Computing sqrt of " << x << " to be " << result << std::endl;
  }
  return result;
}
//mysqrt 1.0.0
#include "mysqrt.h"
#include <iostream>
double sqrt(double x)
{
  if (x <= 0) return 0;
  double result = x;

  for (int i = 0; i < 5; ++i) {
    if (result <= 0) {
      result = 0.1;
    }
    double delta = x - (result * result);
    result = result + 0.5 * delta / result;
    std::cout << i << "Computing sqrt of " << x << " to be " << result << std::endl;
  }
  return result;
}

2. 編寫 MathFunctions/CMakeLists.txt

此次變更屬於補丁(Patch):修正錯誤或優化程式碼,因此更改專案版本到1.0.1
只是變更函式實現方式,原本的函式使用方法不變,可以預期原本的ABI二進制接口並不會更改,具有向後兼容性。

# 設定項目名稱和版本號
project(MathFunctions VERSION 1.0.1)

3. 進入/MathFunctions/build資料夾編譯出動態庫文件

$ cd /MathFunctions/build
$ cmake ..
$ make

可以看到lib資料夾內容中,libmysqrt.so.1 軟鍊結到 libmysqrt.so.1.0.1 ,且不刪除舊版本 libmysqrt.so.1.0.0

kai@esoc:~/2023_iT_CMake/Day13/lib$ tree
.
├── libmysqrt.so -> libmysqrt.so.1
├── libmysqrt.so.1 -> libmysqrt.so.1.0.1
├── libmysqrt.so.1.0.0
└── libmysqrt.so.1.0.1

4. 使用ldd指令查看主程式的鍊結關係
回到 build 資料夾中查看執行檔的鍊結關係可以發現main鍊結的依然是libmysqrt.so.1,但現在libmysqrt.so.1軟鍊結到建構版本是libmysqrt.so.1.0.1,因此可以預期main已經更新到最新版程式。

$ ldd main

kai@esoc:~/2023_iT_CMake/Day13/build$ ldd main 
	linux-vdso.so.1 (0x00007ffc85dd9000)
	libmysqrt.so.1 => /home/kai/2023_iT_CMake/Day13/lib/libmysqrt.so.1 (0x00007fe4c49dc000)<----- 鍊結到的mysqrt⭐
	libstdc++.so.6 => /lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007fe4c4600000)
	libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007fe4c4996000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fe4c4200000)
	libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fe4c48af000)
	/lib64/ld-linux-x86-64.so.2 (0x00007fe4c49e9000)

5. 執行查看結果

$ ./main 100

kai@esoc:~/2023_iT_CMake/Day13/build$ ./main 100
0Computing sqrt of 100 to be 50.5
1Computing sqrt of 100 to be 26.2401
2Computing sqrt of 100 to be 15.0255
3Computing sqrt of 100 to be 10.8404
4Computing sqrt of 100 to be 10.0326
5Computing sqrt of 100 to be 10.0001
6Computing sqrt of 100 to be 10
7Computing sqrt of 100 to be 10
8Computing sqrt of 100 to be 10
9Computing sqrt of 100 to be 10
The square root of 100 is 10

可以發現不用重新編譯就可以執行最新版程式,迭代了10次。

範例1.1.0

接下來這個範例會新增原始碼並更新版本號,而不需要讓主程式重新編譯。

1. 新增程式碼並更新版本
此版本的更新內容是利用c++的函式多載特性,新增一個sqrt()可以讓使用者自定義迭代次數。

  • 更改 MathFunctions/include/mysqrt.h
#ifndef MYSQRT_H
#define MYSQRT_H
double sqrt(double x);
double sqrt(double x, int iterations);
#endif // MYSQRT_H
  • 更改 MathFunctions/src/mysqrt.cpp
#include "mysqrt.h"
#include <iostream>
double sqrt(double x)
{
  if (x <= 0) return 0;
  double result = x;

  static const double iterations = 10;
  for (int i = 0; i < iterations; ++i) {
    if (result <= 0) {
      result = 0.1;
    }
    double delta = x - (result * result);
    result = result + 0.5 * delta / result;
    std::cout << i << "Computing sqrt of " << x << " to be " << result << std::endl;
  }
  return result;
}

double sqrt(double x, int iterations)
{
  if (x <= 0) return 0;
  double result = x;

  for (int i = 0; i < iterations; ++i) {
    if (result <= 0) {
      result = 0.1;
    }
    double delta = x - (result * result);
    result = result + 0.5 * delta / result;
    std::cout << i << "Computing sqrt of " << x << " to be " << result << std::endl;
  }
  return result;
}

2. 編寫 MathFunctions/CMakeLists.txt

此次變更屬於次要(Minor)版本:新增接口或內容,因此更改版本號為1.1.0。
只是新增內容,原本的函式還是可以繼續使用,可以預期原本的ABI二進制接口並不會更改,具有向後兼容性。

project(MathFunctions VERSION 1.1.0 LANGUAGES CXX)

3. 進入/MathFunctions/build資料夾編譯出動態庫文件

$ cmake ..
$ make

kai@esoc:~/2023_iT_CMake/Day13/lib$ tree
.
├── libmysqrt.so -> libmysqrt.so.1
├── libmysqrt.so.1 -> libmysqrt.so.1.1.0
├── libmysqrt.so.1.0.0
├── libmysqrt.so.1.0.1
└── libmysqrt.so.1.1.0

可以看到lib資料夾內容中,libmysqrt.so.1軟鍊結到libmysqrt.so.1.1.0

4. 使用ldd指令查看主程式的鍊結關係

$ ldd main

kai@esoc:~/2023_iT_CMake/Day13/build$ ldd main 
	linux-vdso.so.1 (0x00007ffc85dd9000)
	libmysqrt.so.1 => /home/kai/2023_iT_CMake/Day13/lib/libmysqrt.so.1 (0x00007fe4c49dc000)<----- 鍊結到的mysqrt⭐
	libstdc++.so.6 => /lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007fe4c4600000)
	libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007fe4c4996000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fe4c4200000)
	libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fe4c48af000)
	/lib64/ld-linux-x86-64.so.2 (0x00007fe4c49e9000)

因為沒有重新編譯主程式,因此鍊結的動態庫並不會改變,但是軟鍊結改變了,因此處程式實際鍊結的是最新版本的動態庫libmysqrt.so.1.1.0

5. 執行查看結果

$ ./main 100

kai@esoc:~/2023_iT_CMake/Day13/build$ ./main 100
0Computing sqrt of 100 to be 50.5
1Computing sqrt of 100 to be 26.2401
2Computing sqrt of 100 to be 15.0255
3Computing sqrt of 100 to be 10.8404
4Computing sqrt of 100 to be 10.0326
5Computing sqrt of 100 to be 10.0001
6Computing sqrt of 100 to be 10
7Computing sqrt of 100 to be 10
8Computing sqrt of 100 to be 10
9Computing sqrt of 100 to be 10
The square root of 100 is 10

可以看到使用 1.1.0 版本 mysqrt 動態庫的主程式不用重新編譯就能執行,且輸出不會改變。

範例2.0.0

接下來這個範例會更改sqrt()的使用方式,因此需要讓主程式重新編譯。
1. 更改程式碼並更新版本
此版本的更新內容是使用者必須指定迭代次數,並刪除原本的使用方式。

  • 更改 MathFunctions/include/mysqrt.h
#ifndef MYSQRT_H
#define MYSQRT_H
double sqrt(double x, int iterations);
#endif // MYSQRT_H
  • 更改 MathFunctions/src/mysqrt.cpp
#include "mysqrt.h"
#include <iostream>

double sqrt(double x, int iterations)
{
  if (x <= 0) return 0;
  double result = x;

  for (int i = 0; i < iterations; ++i) {
    if (result <= 0) {
      result = 0.1;
    }
    double delta = x - (result * result);
    result = result + 0.5 * delta / result;
    std::cout << i << "Computing sqrt of " << x << " to be " << result << std::endl;
  }
  return result;
}

  • 更改 src/main.cpp
// A simple program that computes the square root of a number
#include <iostream>
#include "mysqrt.h"

int main(int argc, char* argv[])
{
  if (argc < 2) return 1;
  
  // convert input to double
  const double inputValue = std::stod(argv[1]);
  const double outputValue = sqrt(inputValue, 15);
  std::cout << "The square root of " << inputValue << " is " << outputValue
            << std::endl;
  return 0;
}

2. 編寫 MathFunctions/CMakeLists.txt
此次更新屬於主要(Major)版本:對接口或使用方法進行向後不兼容的更改,因此更新版本號為2.0.0

project(MathFunctions VERSION 2.0.0 LANGUAGES CXX)

3. 進入/MathFunctions/build資料夾編譯出動態庫文件

$ cmake ..
$ make

kai@esoc:~/2023_iT_CMake/Day13/lib$ tree
.
├── libmysqrt.so -> libmysqrt.so.2
├── libmysqrt.so.1 -> libmysqrt.so.1.1.0
├── libmysqrt.so.1.0.0
├── libmysqrt.so.1.0.1
├── libmysqrt.so.1.1.0
├── libmysqrt.so.2 -> libmysqrt.so.2.2.0
└── libmysqrt.so.2.2.0

在 lib 資料夾中可以看到 libmysqrt.so 軟鍊結到的是 libmysqrt.so.2 ,libmysqrt.so.2 又軟鍊結到 libmysqrt.so.2.2.0

4. 編譯主程式
回到 /build 資料夾中編譯主程式,將主程式和動態庫鍊結在一起

$ cmake ..
$ make

5. 使用ldd指令查看編譯出的執行檔的鍊結關係

$ ldd main

kai@esoc:~/2023_iT_CMake/Day13/build$ ldd main 
	linux-vdso.so.1 (0x00007ffd231e2000)
	libmysqrt.so.2 => /home/kai/2023_iT_CMake/Day13/lib/libmysqrt.so.2 (0x00007f8c7dd8d000)<----- 鍊結到的mysqrt⭐
	libstdc++.so.6 => /lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f8c7da00000)
	libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f8c7dd47000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f8c7d600000)
	libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f8c7dc60000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f8c7dd9a000)
  1. 執行main

$ ./main 100

kai@esoc:~/2023_iT_CMake/Day13/build$ ./main 100
0Computing sqrt of 100 to be 50.5
1Computing sqrt of 100 to be 26.2401
2Computing sqrt of 100 to be 15.0255
3Computing sqrt of 100 to be 10.8404
4Computing sqrt of 100 to be 10.0326
5Computing sqrt of 100 to be 10.0001
6Computing sqrt of 100 to be 10
7Computing sqrt of 100 to be 10
8Computing sqrt of 100 to be 10
9Computing sqrt of 100 to be 10
10Computing sqrt of 100 to be 10
11Computing sqrt of 100 to be 10
12Computing sqrt of 100 to be 10
13Computing sqrt of 100 to be 10
14Computing sqrt of 100 to be 10
The square root of 100 is 10

可以發現執行結果是最新的動態庫版本

更新紀錄

上述的範例能夠總結成以下更新紀錄。

版本 內容
1.0.0 一個使用牛頓法求根的庫
1.0.1 將迭代次數增加為10
1.1.0 新增自訂迭代次數的同名重載函數
2.0.0 將以前的固定迭代次數的求根函數刪除,只能用自訂迭代次數

上一篇
[Day 12] 動態庫與版本號
下一篇
[Day 14] 庫的安裝方式
系列文
建構屬於自己的C/C++專案 : 我的30天CMake學習之旅29
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言