banner
yono

yono

哈喽~欢迎光临
follow
github

CMAKEの合理化構造

について#

🎉ついに、keil の緑の死体の圧力の下で、会社は gcc コンパイラに移行する準備が整いました。そして、gcc コンパイラチェーンの統合には CMAKE より良い選択肢はないと思います(もちろん EIDE プラグインも悪くはありません)。長い間独立して探求し、ついに keil を完全に排除しました。eclipse はまだ完全には排除できませんが、TI の MCU には CCS が必要です。いつ vscode が全面的に普及するのでしょうか?

最近、元のプロジェクトを完全な CMAKE プロジェクトに変換し、最適化できるプロジェクト構造のポイントをいくつか発見しました。

ツール#

ブログのファイルサービスページには、ポータブル vscode のダウンロードがあるはずで、必要なツールがすべて統合されています。緑色でインストール不要です。

==1_初回起動時は.bat を実行してください == というファイルがあり、ダブルクリックして実行すると、vscode が直接使用できるようになります。このスクリプトの主な役割は、管理者モードに昇格し、各ツールチェーンのパスを自動的に環境変数に追加することです。

ファイルサービス - 土星環の基地

vscode ワークスペースの合理化#

.code-workspace ファイルの settings セクションに以下の部分を追加できます。

    "settings": {
        "C_Cpp.formatting": "clangFormat",
        "cmake.configureEnvironment": {
            "PROGRAM_NAME": "H7_GCC_PIONEER"
        }
    },

ここで指定されているフォーマッタは clangFormat で、このフォーマッタの実行可能ファイルは ==1_初回起動時は.bat を実行してください == の際に環境変数に追加されます。

CMAKE の環境変数を指定しており、これは生成される書き込みファイルの名前を設定するためのものです。

主 cmakelist、つまりルートディレクトリには、以前以下の内容がありました。

set(CMAKE_PROJECT_NAME DebugBuild)

以下のように変更します。

if(DEFINED ENV{PROGRAM_NAME})
    set(CMAKE_PROJECT_NAME $ENV{PROGRAM_NAME})
else()
    message(WARNING "PROGRAM_NAME環境変数が設定されていません。デフォルトのプロジェクト名を使用します。")
    set(CMAKE_PROJECT_NAME "DefaultProjectName")
endif()

これにより、ワークスペースの.code-workspaceファイルにあるPROGRAM_NAMEが基準になります。
この操作は、プロジェクトの開発者が CMAKE 関連のファイルをできるだけ変更しないようにするためのもので、今後もこの目的に沿った他の操作があります。

主なビルドの分岐#

CMAKE_BUILD_TYPE については、CMAKE プラグインから実際のビルドが渡されます。しかし、このビルドタイプに加えて、いくつかのカスタムビルドタイプも希望しており、以下の操作が可能です。

実行可能な主ビルドの前方でカスタム属性変数を使用します。

set(CMAKE_BUILD_BL "YES")
set(CMAKE_BUILD_BL "NO")

ツールチェーン指定ファイルでは、以下のように操作できます(もちろん他のファイルでも同様で、主ビルドのこれらの属性変数はサブディレクトリやサブビルドに伝播します)。

if(CMAKE_BUILD_BL MATCHES YES)
    set(CMAKE_C_LINK_FLAGS "${CMAKE_C_LINK_FLAGS} -T \"${CMAKE_SOURCE_DIR}/CPU/GNU/stm32H743XI_INCBL.ld\"")   # リンクスクリプトを追加
endif()
if(CMAKE_BUILD_BL MATCHES NO)
    set(CMAKE_C_LINK_FLAGS "${CMAKE_C_LINK_FLAGS} -T \"${CMAKE_SOURCE_DIR}/CPU/GNU/stm32H743XI.ld\"")   # リンクスクリプトを追加
endif()

この操作により、2 つの異なるリンカースクリプトを制御します。YES の場合のリンカースクリプトには bootload 部分が含まれ、主に出荷時の書き込みに使用されます。NO の場合は bootload 部分が含まれず、アップグレード用です。これにより、操作が一部削減され、EIDE プラグインではこのように便利にはできません。毎回手動でスクリプトを少し変更する必要があります。もちろん、開発者が CMAKE 関連のファイルをできるだけ変更しないようにするために、前述の操作のように新しい環境変数を作成することもできます。

主ビルドでの定義後のタスク#

私の主ビルドの最後には以下の 2 行があります。

# 個別のファイルにコンパイルフラグを追加
include("${CMAKE_CURRENT_LIST_DIR}/8_WorkSpace/CMake/toolCmake/extra-compile-flags.cmake")

# ビルド後のタスクを実行
include("${CMAKE_CURRENT_LIST_DIR}/8_WorkSpace/CMake/toolCmake/post-build-tasks.cmake")

extra-compile-flags.cmake#

このファイルの内容は以下の通りです。

include(${CMAKE_CURRENT_LIST_DIR}/cmake_func/functions.cmake)

set_compile_flags_for_matching_files(${CMAKE_PROJECT_NAME} "6_Rtos|7_Exlib" "-w")
set_compile_flags_for_matching_files(user_src "6_Rtos|7_Exlib" "-w")

この目的は、主ビルドプロジェクト${CMAKE_PROJECT_NAME}およびソースを引き入れたプロジェクトuser_srcにおいて、6_Rtos または 7_Exlib フォルダ内のソースコードにすべて-wのコンパイルフラグを追加することです。これはすべての警告を無視することを意味します。私たちは超厳格な全警告を有効にしているため、オペレーティングシステムや他の人が書いたライブラリにいくつかの警告があるかもしれず、それが非常に煩わしく、変更できないため、このような操作があります。注意すべきは、ソースを引き入れたプロジェクトuser_srcは元々すべての部分がINTERFACE属性で定義されているため、user_src内のソースコードにコンパイルフラグを正常に追加できないことです。そのため、target_sources部分は現在PUBLIC属性で定義されるべきであり、受け入れられない副作用はまだ発見されていません。

最初の引数はプロジェクト名、2 番目の引数はこれらのフィールドを含むソースファイルのパス、3 番目の引数は追加したいコンパイルフラグです。これにより新しいファイル functions.cmake が導入されます。

# FUNC

# ヘッダーファイルを再帰的に含む関数
function(include_sub_directories_recursively root_dir)
    if (IS_DIRECTORY ${root_dir})               # 現在のパスがディレクトリかどうか、そうであれば含める
        message("include dir: " ${root_dir})
        target_include_directories(${PROJECT_NAME} INTERFACE
        ${root_dir}
        )
    endif()

    file(GLOB ALL_SUB RELATIVE ${root_dir} ${root_dir}/*) # 現在のディレクトリ内のすべてのファイルを取得
    foreach(sub ${ALL_SUB})
        if (IS_DIRECTORY ${root_dir}/${sub})
            include_sub_directories_recursively(${root_dir}/${sub}) # サブディレクトリを再帰的に呼び出し、含める
        endif()
    endforeach()
endfunction()

# 特定のターゲットに、パスにキーワードを含むソースファイルに期待するフラグを追加
function(set_compile_flags_for_matching_files target_name keywords compile_flags)
    # ターゲットの元のソースファイルリストを取得
    get_target_property(src_list ${target_name} SOURCES)

    # ターゲットが存在し、ソースファイルがあるか確認
    if(NOT src_list)
        message(WARNING "ターゲット '${target_name}' は存在しないか、ソースがありません。")
        return()
    endif()

    # コンパイルフラグを設定する必要があるファイルを格納する新しいリストを作成
    set(filtered_src_list)

    # 元のソースファイルリストをループし、必要なファイルをフィルタリング
    foreach(src_file IN LISTS src_list)
        if("${src_file}" MATCHES "${keywords}")
            list(APPEND filtered_src_list ${src_file})
        endif()
    endforeach()

    # フィルタリングされたソースファイルにコンパイルフラグを設定
    foreach(src_file IN LISTS filtered_src_list)
        set_source_files_properties(${src_file} PROPERTIES COMPILE_FLAGS "${compile_flags}")
    endforeach()
endfunction()


# FUNC END

これは私が cmake 関数を書くために専用に作成したファイルですが、詳細は省略します。必要なときに関数を include します。

post-build-tasks.cmake#

このファイルは、ビルド後のタスクを管理しやすくするためのもので、内容は以下の通りです。

# ビルド後のタスクを個別に管理

# ツールチェーンツールを定義
set(OBJCOPY arm-none-eabi-objcopy)
set(OBJDUMP arm-none-eabi-objdump)

# カスタムコマンドを追加してbinおよびhexファイルを生成
add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD
    COMMAND ${OBJCOPY} -O binary $<TARGET_FILE:${CMAKE_PROJECT_NAME}> ${CMAKE_PROJECT_NAME}.bin
    COMMAND ${OBJCOPY} -O ihex $<TARGET_FILE:${CMAKE_PROJECT_NAME}> ${CMAKE_PROJECT_NAME}.hex
    COMMAND  ${CMAKE_CURRENT_LIST_DIR}/gccMapView.exe ${CMAKE_BINARY_DIR}
    COMMENT "elfからbinおよびhexファイルを生成中"
)

これは bin ファイルと hex ファイルを生成するためのものです。
その後、gccMapView.exe を実行しました。これは私が map を整理するために作った小さなソフトウェアで、GCC のオリジナルの map は非常に見栄えが悪いため、この小さなソフトウェアがあります。オープンソースのアドレスはここにあり、効果は非常に良好です。

stbanana/gcc-elf-link-sort: A tool for sorting the symbols of .elf by address

原 map.png

整理出力.png

プロジェクトソースの引き入れにおける自動化のための探索#

プロジェクトソースはヘッダーファイルのパスを探索し、含める部分があります。

# cmake FUNC
include(${CMAKE_CURRENT_LIST_DIR}/cmake_func/functions.cmake)

# ヘッダーファイルを再帰的に含む
include_sub_directories_recursively(${CMAKE_CURRENT_LIST_DIR}/../../../1_App)
include_sub_directories_recursively(${CMAKE_CURRENT_LIST_DIR}/../../../2_Contorl)
include_sub_directories_recursively(${CMAKE_CURRENT_LIST_DIR}/../../../3_Module)
include_sub_directories_recursively(${CMAKE_CURRENT_LIST_DIR}/../../../4_Driver)

これにより cmake 関数が呼び出されるため、統一された関数定義ファイル内にあり、これら 4 つのフォルダ内のすべてのパスを含め、サブディレクトリもすべて追加します。

さらに以下の部分があります。

# すべてのソースファイルを再帰的に検索
file(GLOB_RECURSE 1_APP_SRC ${CMAKE_CURRENT_LIST_DIR}/../../../1_App/*.c)
file(GLOB_RECURSE 2_CONTORL_SRC ${CMAKE_CURRENT_LIST_DIR}/../../../2_Contorl/*.c)
file(GLOB_RECURSE 3_MODULE_SRC ${CMAKE_CURRENT_LIST_DIR}/../../../3_Module/*.c)
file(GLOB_RECURSE 4_DRIVER_SRC ${CMAKE_CURRENT_LIST_DIR}/../../../4_Driver/*.c)
file(GLOB_RECURSE 5_DRIVER_SRC ${CMAKE_CURRENT_LIST_DIR}/../../../5_PhysicalChip/Stm32H7/*.c)

target_sources(${CMAKE_PROJECT_NAME} PUBLIC
    ../../../main.c
    ${1_APP_SRC}
    ${2_CONTORL_SRC}
    ${3_MODULE_SRC}
    ${4_DRIVER_SRC}
    ${5_DRIVER_SRC}
)

これは、これら 4 つのフォルダ内のすべてのソースコードをソースファイルに追加するためのものです。

さらに、より低レベルの RTOS やライブラリの部分では、ソースコードと include パスを手動で指定しています。

これを行った後、すべての無駄なビジネスソースコードを自由に記述・変更できるようになりました。以前は名前を変更したり構造を変更したりするために多くのプロジェクト設定を変更する必要がありましたが、今ではビジネスコードの開発が完全にスムーズに行えるようになりました。ビジネスコードとドライバサポートがさらに分離されました。

ここで前述の変更があり、target_sources部分は現在PUBLIC属性で定義されるべきです。そうでなければ、個別のファイルのコンパイルフラグを指定できません。INTERFACE属性は実際のコンパイルでは行われますが、主ビルドプロジェクトに掛かります。しかし、CMAKE システム内ではこのファイルインスタンスが存在しません。本質的に、これはソースを引き入れたプロジェクトuser_srcに仮に掛かっていますが、user_srcINTERFACEインターフェースの仮想ライブラリであり、実際にはコンパイルされないため、すべてのソースコードはプロジェクトuser_src全体として主ビルドプロジェクトにリンクされ、正確なファイルを見つけることができません。ただ主ビルドプロジェクトのコンパイルフラグを継承してコンパイルに参加するだけです。

この記事は Mix Space によって xLog に同期更新されました。元のリンクは https://www.yono233.cn/posts/novel/24_11_25_CMAKE%E7%9A%84%E5%90%88%E7%90%86%E5%8C%96%E7%BB%93%E6%9E%84

読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。