2022年6月11日 星期六

Table Of Content for "CMake Tutorial"



Step 1: A Basic Starting Point
Step 2: Adding a Library
- Step 2: Adding A Library As An Option
Step 3: Adding Usage Requirements for a Library
Step 4: Installing and Testing
Step 5: Adding System Introspection
Step 6: Adding a Custom Command and Generated File
Step 7: Packaging an Installer
Step 8: Adding Support for a Testing Dashboard
Step 9: Selecting Static or Shared Libraries
Step 10: Adding Generator Expressions
Step 11: Adding Export Configuration
Step 12: Packaging Debug and Release



2022年6月3日 星期五

CMake - Step 1: A Basic Starting Point


本文是CMake Tutorial的心得, CMake是一個用於build, test與package的cross-platform tool, 基本上是透過撰寫CMakeLists來描述如何build, test與package專案,接著會產生makefile, 再透過makefile產生最終的target(executable, library, or both)
讓我們從最簡單的CMakeLists.txt開始,基本上CMakeLists.txt的指令是不分大小寫的
# Require a minimum version of cmake.
# cmake_minimum_required(VERSION <min>>[...<policy_max>] [FATAL_ERROR])
cmake_minimum_required(VERSION 3.10)

# Set the name of the project.
# project(<PROJECT-NAME< [<anguage-name>...])
# project(<PROJECT-NAME>
#         [VERSION <major>[.<minor>[.<patch>[.<tweak>]]]]
#         [DESCRIPTION <project-description-string>]
#         [HOMEPAGE_URL <url-string>]
#         [LANGUAGES <language-name>...])
project(Tutorial VERSION 0.1.2 DESCRIPTION "This is Brook 1st CMake")

# Add an executable to the project using the specified source files.
# add_executable(<name> [WIN32] [MACOSX_BUNDLE]
#                [EXCLUDE_FROM_ALL]
#                [source1] [source2 ...])
add_executable(Tutorial hello.c)

hello.c
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
        printf("hello world\n");
        return 0;
}

有了CMakeLists.txt與source code, 就可以使用cmake [options] <path-to-source>建立Makefile, 並使用cmake --build <dir>去build targe
[brook@:~/Projects/cmake]$ tree
.
`-- src
    |-- CMakeLists.txt
    `-- hello.c

1 directory, 2 files
[brook@:~/Projects/cmake]$ mkdir build && cd build
[brook@:~/Projects/cmake/build]$ cmake ../src/
-- The C compiler identification is GNU 7.5.0
-- The CXX compiler identification is GNU 7.5.0
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /build/brook/Projects/cmake/build
[brook@:~/Projects/cmake/build]$ ls
CMakeCache.txt  CMakeFiles  Makefile  cmake_install.cmake
[brook@:~/Projects/cmake/build]$ cmake --build .
Scanning dependencies of target Tutorial
[ 50%] Building C object CMakeFiles/Tutorial.dir/hello.c.o
[100%] Linking C executable Tutorial
[100%] Built target Tutorial
[brook@:~/Projects/cmake/build]$ ls
CMakeCache.txt  CMakeFiles  Makefile  Tutorial  cmake_install.cmake
[brook@:~/Projects/cmake/build]$ ./Tutorial
hello world


Adding a Version Number and Configured Header File

前面的CMakeLists.txt中描述了project(Tutorial VERSION 0.1.2 DESCRIPTION "This is Brook 1st CMake"), 但是沒在C code中被用到, 所以, 我們將這些變數加入configure a header file 並傳遞給source code
# Require a minimum version of cmake.
# cmake_minimum_required(VERSION <min>>[...<policy_max>] [FATAL_ERROR])
cmake_minimum_required(VERSION 3.10)

# Set the name of the project.
# project(<PROJECT-NAME< [<anguage-name>...])
# project(<PROJECT-NAME>
#         [VERSION <major>[.<minor>[.<patch>[.<tweak>]]]]
#         [DESCRIPTION <project-description-string>]
#         [LANGUAGES <language-name>...])
project(Tutorial VERSION 0.1.2 DESCRIPTION "This is Brook 1st CMake")

# Add an executable to the project using the specified source files.
# add_executable(<name> [WIN32] [MACOSX_BUNDLE]
#                [EXCLUDE_FROM_ALL]
#                [source1] [source2 ...])
add_executable(Tutorial hello.c)


# Copy a file to another location and modify its contents.
# configure_file(<input> <output>
#                [NO_SOURCE_PERMISSIONS | USE_SOURCE_PERMISSIONS |
#                 FILE_PERMISSIONS <permissions>...]
#                [COPYONLY] [ESCAPE_QUOTES] [@ONLY]
#                [NEWLINE_STYLE [UNIX|DOS|WIN32|LF|CRLF] ])
configure_file(Config.h.in Config.h)

# Add include directories to a target.
# target_include_directories(&;lt;target> [SYSTEM] [AFTER|BEFORE]
#   &;lt;INTERFACE|PUBLIC|PRIVATE> [items1...]
#   [&;lt;INTERFACE|PUBLIC|PRIVATE> [items2...] ...])
target_include_directories(Tutorial PUBLIC "${PROJECT_BINARY_DIR}")


首先先透過configure_file()設定configure file的input與output, 在CMake建立Makefile過程中, 就會將configure的input變數取代後, 產生configure的output檔. 由於configure header file要被source code給include進來, 所以還得透過target_include_directories()設定對應的include path. 相關執行步驟如下:
[brook@:~/Projects/cmake]$ tree
.
`-- src
    |-- CMakeLists.txt
    |-- Config.h.in
    `-- hello.c

1 directory, 3 files
[brook@:~/Projects/cmake]$ cat src/Config.h.in
// the configured options and settings for Tutorial

#define VERSION_MAJOR @Tutorial_VERSION_MAJOR@
#define VERSION_MINOR @Tutorial_VERSION_MINOR@

[brook@:~/Projects/cmake]$ cat src/hello.c
#include <stdio.h>
#include <stdlib.h>
#include "Config.h"

int main(int argc, char *argv[])
{
        printf("hello world(version: %d.%d)\n", VERSION_MAJOR, VERSION_MINOR);
        return 0;
}

[brook@:~/Projects/cmake]$ mkdir build && cd build
[brook@:~/Projects/cmake/build]$ cmake ../src/
-- The C compiler identification is GNU 7.5.0
-- The CXX compiler identification is GNU 7.5.0
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /build/brook/Projects/cmake/build
[brook@:~/Projects/cmake/build]$ ls
CMakeCache.txt  CMakeFiles  Config.h  Makefile  cmake_install.cmake
[brook@:~/Projects/cmake/build]$ cmake --build .
Scanning dependencies of target Tutorial
[ 50%] Building C object CMakeFiles/Tutorial.dir/hello.c.o
[100%] Linking C executable Tutorial
[100%] Built target Tutorial
[brook@:~/Projects/cmake/build]$ ./Tutorial
hello world(version: 0.1)
[brook@:~/Projects/cmake/build]$ cat Config.h
// the configured options and settings for Tutorial
#define VERSION_MAJOR 0
#define VERSION_MINOR 1

在這裡我們有用到幾個CMake的變數, 包含PROJECT_BINARY_DIR, <PROJECT-NAME>_VERSION_MAJOR, PROJECT_BINARY_DIR等等, 這些都可以參考cmake-variables(7)取得更多的變數與相關細節, 我這裡就簡單介紹幾個基本的, 並透過Configure header file讓大家稍微理解一下對應的值
[brook@:~/Projects/cmake/build]$ cat ../src/Config.h.in
// the configured options and settings for Tutorial
#define VERSION_MAJOR @Tutorial_VERSION_MAJOR@
#define VERSION_MINOR @Tutorial_VERSION_MINOR@

// <PROJECT-NAME>_BINARY_DIR
// A variable is created with the name used in the project() command,
// and is the binary directory for the project.
#define Tutorial_BINARY_DIR @Tutorial_BINARY_DIR@

// <PROJECT-NAME>_SOURCE_DIR
// Top level source directory for the named project.
// A variable is created with the name used in the project() command,
// and is the source directory for the project.
#define Tutorial_SOURCE_DIR @Tutorial_SOURCE_DIR@

// <PROJECT-NAME>_VERSION
// Value given to the VERSION option of the most recent call
// to the project() command with project name <PROJECT-NAME>
#define Tutorial_VERSION "@Tutorial_VERSION@"
#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@
#define Tutorial_VERSION_PATCH @Tutorial_VERSION_PATCH@
#define Tutorial_VERSION_TWEAK @Tutorial_VERSION_TWEAK@

// Full path to build directory for project.
// This is the binary directory of the most recent project() command.
#define PROJECT_BINARY_DIR @PROJECT_BINARY_DIR@

// Short project description given to the project command.
// This is the description given to the most recent project() command.
#define PROJECT_DESCRIPTION "@PROJECT_DESCRIPTION@"

// Name of the project given to the project command.
// This is the name given to the most recent project() command.
#define PROJECT_NAME @PROJECT_NAME@

// Top level source directory for the current project.
// This is the source directory of the most recent project() command.
#define PROJECT_SOURCE_DIR @PROJECT_SOURCE_DIR@

// Value given to the VERSION option of the most recent call to the project() command
#define PROJECT_VERSION "@PROJECT_VERSION@"
#define PROJECT_VERSION_MAJOR @PROJECT_VERSION_MAJOR@
#define PROJECT_VERSION_MINOR @PROJECT_VERSION_MINOR@
#define PROJECT_VERSION_PATCH @PROJECT_VERSION_PATCH@
#define PROJECT_VERSION_TWEAK @PROJECT_VERSION_TWEAK@

[brook@:~/Projects/cmake/build]$ cmake ../src/
-- Configuring done
-- Generating done
-- Build files have been written to: /build/brook/Projects/cmake/build
[brook@:~/Projects/cmake/build]$ cat Config.h
// the configured options and settings for Tutorial
#define VERSION_MAJOR 0
#define VERSION_MINOR 1

// <PROJECT-NAME>_BINARY_DIR
// A variable is created with the name used in the project() command,
// and is the binary directory for the project.
#define Tutorial_BINARY_DIR /build/brook/Projects/cmake/build

// <PROJECT-NAME>_SOURCE_DIR
// Top level source directory for the named project.
// A variable is created with the name used in the project() command,
// and is the source directory for the project.
#define Tutorial_SOURCE_DIR /build/brook/Projects/cmake/src

// <PROJECT-NAME>_VERSION
// Value given to the VERSION option of the most recent call
// to the project() command with project name <PROJECT-NAME>
#define Tutorial_VERSION "0.1.2"
#define Tutorial_VERSION_MAJOR 0
#define Tutorial_VERSION_MINOR 1
#define Tutorial_VERSION_PATCH 2
#define Tutorial_VERSION_TWEAK

// Full path to build directory for project.
// This is the binary directory of the most recent project() command.
#define PROJECT_BINARY_DIR /build/brook/Projects/cmake/build

// Short project description given to the project command.
// This is the description given to the most recent project() command.
#define PROJECT_DESCRIPTION "This is Brook 1st CMake"

// Name of the project given to the project command.
// This is the name given to the most recent project() command.
#define PROJECT_NAME Tutorial

// Top level source directory for the current project.
// This is the source directory of the most recent project() command.
#define PROJECT_SOURCE_DIR /build/brook/Projects/cmake/src

// Value given to the VERSION option of the most recent call to the project() command
#define PROJECT_VERSION "0.1.2"
#define PROJECT_VERSION_MAJOR 0
#define PROJECT_VERSION_MINOR 1
#define PROJECT_VERSION_PATCH 2
#define PROJECT_VERSION_TWEAK




2022年5月7日 星期六

Linux Kernel(2.1)- MAJRO NUMBER RESERVED FOR DYNAMIC ASSIGNMENT


Documentation/admin-guide/devices.txt 文檔中描述了各個major number的用途, 而這文章的重點是dynamic的範圍從234~254與384~511
 234-254 char	RESERVED FOR DYNAMIC ASSIGNMENT
		Character devices that request a dynamic allocation of major number will
		take numbers starting from 254 and downward.

 384-511 char	RESERVED FOR DYNAMIC ASSIGNMENT
		Character devices that request a dynamic allocation of major
		number will take numbers starting from 511 and downward,
		once the 234-254 range is full.

相關的代碼如下:
__register_chrdev_region()
  |-> find_dynamic_major()
    |-> 254 ~ 234 or 511 ~ 384 有空的就拿來用

int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,
                        const char *name)
{
  struct char_device_struct *cd;
  cd = __register_chrdev_region(0, baseminor, count, name);
  if (IS_ERR(cd))
    return PTR_ERR(cd);
  *dev = MKDEV(cd->major, cd->baseminor);
  return 0;
}

static struct char_device_struct *
__register_chrdev_region(unsigned int major, unsigned int baseminor,
                           int minorct, const char *name)
{
  struct char_device_struct *cd, *curr, *prev = NULL;
  int ret;
  int i;

  if (major >= CHRDEV_MAJOR_MAX) {
    pr_err("CHRDEV \"%s\" major requested (%u) is greater than the maximum (%u)\n",
        name, major, CHRDEV_MAJOR_MAX-1);
    return ERR_PTR(-EINVAL);
  }

  if (minorct > MINORMASK + 1 - baseminor) {
    pr_err("CHRDEV \"%s\" minor range requested (%u-%u) is out of range of maximum range (%u-%u) for a single major\n",
      name, baseminor, baseminor + minorct - 1, 0, MINORMASK);
    return ERR_PTR(-EINVAL);
  }

    cd = kzalloc(sizeof(struct char_device_struct), GFP_KERNEL);
    if (cd == NULL)
        return ERR_PTR(-ENOMEM);

    mutex_lock(&chrdevs_lock);

    if (major == 0) {
        ret = find_dynamic_major();
        if (ret < 0) {
            pr_err("CHRDEV \"%s\" dynamic allocation region is full\n",
                 name);
            goto out;
        }
        major = ret;
    }

    ret = -EBUSY;
    i = major_to_index(major);
    for (curr = chrdevs[i]; curr; prev = curr, curr = curr->next) {
        if (curr->major < major)
            continue;

        if (curr->major > major)
            break;

        if (curr->baseminor + curr->minorct <= baseminor)
            continue;

        if (curr->baseminor >= baseminor + minorct)
            break;

        goto out;
    }

    cd->major = major;
    cd->baseminor = baseminor;
    cd->minorct = minorct;
    strlcpy(cd->name, name, sizeof(cd->name));

    if (!prev) {
        cd->next = curr;
        chrdevs[i] = cd;
    } else {
        cd->next = prev->next;
        prev->next = cd;
    }

    mutex_unlock(&chrdevs_lock);
    return cd;
out:
    mutex_unlock(&chrdevs_lock);
    kfree(cd);
    return ERR_PTR(ret);
}

/* fs/char_dev.c */
#define CHRDEV_MAJOR_MAX 512
/* Marks the bottom of the first segment of free char majors */
#define CHRDEV_MAJOR_DYN_END 234
/* Marks the top and bottom of the second segment of free char majors */
#define CHRDEV_MAJOR_DYN_EXT_START 511
#define CHRDEV_MAJOR_DYN_EXT_END 384

#define CHRDEV_MAJOR_HASH_SIZE 255
static struct char_device_struct {
    struct char_device_struct *next;
    unsigned int major;
    unsigned int baseminor;
    int minorct;
    char name[64];
    struct cdev *cdev;        /* will die */
} *chrdevs[CHRDEV_MAJOR_HASH_SIZE];


static int find_dynamic_major(void)
{
    int i;
    struct char_device_struct *cd;

              /* from 254 ~ 234 */
    for (i = ARRAY_SIZE(chrdevs)-1; i >= CHRDEV_MAJOR_DYN_END; i--) {
        if (chrdevs[i] == NULL)
            return i;
    }

                /* from 511 ~ 384 */
    for (i = CHRDEV_MAJOR_DYN_EXT_START;
       i >= CHRDEV_MAJOR_DYN_EXT_END; i--) {
        for (cd = chrdevs[major_to_index(i)]; cd; cd = cd->next)
            if (cd->major == i)
                break;

        if (cd == NULL)
            return i;
    }

    return -EBUSY;
}


熱門文章