summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/linux.yml9
-rw-r--r--CHANGELOG.md212
-rw-r--r--core/src/cli/xmake.lua6
-rwxr-xr-xcore/src/cli/xmake.sh3
m---------core/src/tbox/tbox0
-rw-r--r--core/src/xmake/binutils/ar/extractlib.c2
-rw-r--r--core/src/xmake/binutils/bin2c.c4
-rw-r--r--core/src/xmake/binutils/coff/bin2coff.c4
-rw-r--r--core/src/xmake/binutils/elf/bin2elf.c146
-rw-r--r--core/src/xmake/binutils/elf/prefix.h164
-rw-r--r--core/src/xmake/binutils/extractlib.c2
-rw-r--r--core/src/xmake/binutils/macho/bin2macho.c4
-rw-r--r--core/src/xmake/binutils/prefix.h43
-rw-r--r--core/src/xmake/binutils/readsyms.c4
-rw-r--r--core/src/xmake/engine.c6
-rw-r--r--core/src/xmake/engine_pool.c21
-rw-r--r--core/src/xmake/io/file_close.c2
-rw-r--r--core/src/xmake/io/file_write.c2
-rw-r--r--core/src/xmake/os/cpuinfo.c4
-rw-r--r--core/src/xmake/os/meminfo.c5
-rw-r--r--core/src/xmake/process/open.c14
-rw-r--r--core/src/xmake/semver/select.c120
-rw-r--r--core/src/xmake/utf8/width.c6
-rw-r--r--core/xmake.lua2
-rwxr-xr-xcore/xmake.sh2
-rwxr-xr-xscripts/get.ps12
-rw-r--r--scripts/rpmbuild/xmake.spec2
-rw-r--r--tests/actions/addon/custom-include/addon.lua2
-rw-r--r--tests/actions/addon/custom-include/includes/check/xmake.lua4
-rw-r--r--tests/actions/addon/custom-module/addon.lua2
-rw-r--r--tests/actions/addon/custom-module/modules/greeting.lua3
-rw-r--r--tests/actions/addon/custom-override/addon.lua2
-rw-r--r--tests/actions/addon/custom-override/plugins/format/main.lua3
-rw-r--r--tests/actions/addon/custom-override/plugins/format/xmake.lua6
-rw-r--r--tests/actions/addon/custom-package/addon.lua2
-rw-r--r--tests/actions/addon/custom-package/includes/packages/src/include/hello_utils.h6
-rw-r--r--tests/actions/addon/custom-package/includes/packages/xmake.lua13
-rw-r--r--tests/actions/addon/custom-plugin/addon.lua2
-rw-r--r--tests/actions/addon/custom-plugin/plugins/hello_addon/main.lua5
-rw-r--r--tests/actions/addon/custom-plugin/plugins/hello_addon/xmake.lua5
-rw-r--r--tests/actions/addon/custom-rule/addon.lua2
-rw-r--r--tests/actions/addon/custom-rule/modules/greeting.lua3
-rw-r--r--tests/actions/addon/custom-rule/rules/hello/xmake.lua6
-rw-r--r--tests/actions/addon/custom-template/addon.lua2
-rw-r--r--tests/actions/addon/custom-template/templates/c/customaddon/hello/src/main.c6
-rw-r--r--tests/actions/addon/custom-template/templates/c/customaddon/hello/xmake.lua5
-rw-r--r--tests/actions/addon/custom-toolchain/addon.lua6
-rw-r--r--tests/actions/addon/custom-toolchain/src/modules/core/tools/mycl6x.lua12
-rw-r--r--tests/actions/addon/custom-toolchain/src/modules/detect/tools/find_mycl6x.lua15
-rw-r--r--tests/actions/addon/custom-toolchain/src/toolchains/my-c6000/xmake.lua19
-rw-r--r--tests/actions/addon/projects/autofetch-badname/xmake.lua5
-rw-r--r--tests/actions/addon/projects/autofetch-build/src/main.c14
-rw-r--r--tests/actions/addon/projects/autofetch-build/xmake.lua14
-rw-r--r--tests/actions/addon/projects/autofetch-lockmiss/xmake-addons.lock9
-rw-r--r--tests/actions/addon/projects/autofetch-lockmiss/xmake.lua4
-rw-r--r--tests/actions/addon/projects/autofetch/xmake.lua8
-rw-r--r--tests/actions/addon/projects/custom-package/src/main.c6
-rw-r--r--tests/actions/addon/projects/custom-package/xmake.lua9
-rw-r--r--tests/actions/addon/test.lua507
-rw-r--r--tests/modules/binutils/test.lua124
-rw-r--r--tests/modules/project_depend/test.lua19
-rwxr-xr-xtests/modules/semver/test.lua92
-rw-r--r--tests/projects/c++/modules/precompile_reduced_bmi/src/base.mpp5
-rw-r--r--tests/projects/c++/modules/precompile_reduced_bmi/src/hello.mpp7
-rw-r--r--tests/projects/c++/modules/precompile_reduced_bmi/src/main.cpp5
-rw-r--r--tests/projects/c++/modules/precompile_reduced_bmi/test.lua107
-rw-r--r--tests/projects/c++/modules/precompile_reduced_bmi/xmake.lua6
-rw-r--r--tests/projects/c++/modules/stdmodules_deps/src/bar.mpp10
-rw-r--r--tests/projects/c++/modules/stdmodules_deps_ordering/src/foo.cpp5
-rw-r--r--tests/projects/c++/modules/stdmodules_deps_ordering/src/foo.mpp3
-rw-r--r--tests/projects/c++/modules/stdmodules_deps_ordering/src/main.cpp8
-rw-r--r--tests/projects/c++/modules/stdmodules_deps_ordering/test.lua1
-rw-r--r--tests/projects/c++/modules/stdmodules_deps_ordering/xmake.lua19
-rw-r--r--tests/projects/c++/modules/test_base.lua12
-rw-r--r--tests/projects/embed/esp32/blink/boards/esp32c3/board.h7
-rw-r--r--tests/projects/embed/esp32/blink/src/main.c26
-rw-r--r--tests/projects/embed/esp32/blink/xmake-addons.lua2
-rw-r--r--tests/projects/embed/esp32/blink/xmake.lua15
-rw-r--r--tests/projects/other/bin2c/src/asset.bin1
-rw-r--r--tests/projects/other/bin2c/src/asset2.bin1
-rw-r--r--tests/projects/other/bin2c/src/main.c29
-rw-r--r--tests/projects/other/bin2c/test.lua2
-rw-r--r--tests/projects/other/bin2c/transform.lua5
-rw-r--r--tests/projects/other/bin2c/xmake.lua10
-rw-r--r--tests/projects/other/bin2obj/src/asset.bin1
-rw-r--r--tests/projects/other/bin2obj/src/asset2.bin1
-rw-r--r--tests/projects/other/bin2obj/src/main.c50
-rw-r--r--tests/projects/other/bin2obj/test.lua2
-rw-r--r--tests/projects/other/bin2obj/transform.lua5
-rw-r--r--tests/projects/other/bin2obj/xmake.lua8
-rw-r--r--xmake/actions/addon/main.lua334
-rw-r--r--xmake/actions/addon/xmake.lua49
-rw-r--r--xmake/actions/create/main.lua40
-rw-r--r--xmake/actions/create/template.lua80
-rw-r--r--xmake/actions/require/xmake.lua3
-rw-r--r--xmake/core/base/binutils.lua4
-rw-r--r--xmake/core/base/interpreter.lua63
-rw-r--r--xmake/core/base/os.lua17
-rw-r--r--xmake/core/base/poller.lua16
-rw-r--r--xmake/core/base/scheduler.lua14
-rw-r--r--xmake/core/base/semver.lua10
-rw-r--r--xmake/core/base/task.lua69
-rw-r--r--xmake/core/base/thread.lua2
-rw-r--r--xmake/core/language/language.lua2
-rw-r--r--xmake/core/package/addon.lua826
-rw-r--r--xmake/core/package/component.lua2
-rw-r--r--xmake/core/package/package.lua219
-rw-r--r--xmake/core/platform/menu.lua2
-rw-r--r--xmake/core/platform/platform.lua2
-rw-r--r--xmake/core/project/addons.lua140
-rw-r--r--xmake/core/project/option.lua10
-rw-r--r--xmake/core/project/policy.lua8
-rw-r--r--xmake/core/project/project.lua177
-rw-r--r--xmake/core/project/rule.lua229
-rw-r--r--xmake/core/project/target.lua14
-rw-r--r--xmake/core/sandbox/modules/import/core/base/scheduler.lua6
-rw-r--r--xmake/core/sandbox/modules/import/core/base/semver.lua9
-rw-r--r--xmake/core/sandbox/modules/import/core/package/addon.lua96
-rw-r--r--xmake/core/sandbox/modules/import/core/project/addons.lua35
-rw-r--r--xmake/core/sandbox/modules/import/core/project/project.lua4
-rw-r--r--xmake/core/sandbox/modules/import/core/sandbox/module.lua62
-rw-r--r--xmake/core/sandbox/modules/import/core/sandbox/sandbox.lua13
-rw-r--r--xmake/core/sandbox/modules/import/core/tool/compiler.lua4
-rw-r--r--xmake/core/sandbox/modules/import/lib/detect/find_directory.lua2
-rw-r--r--xmake/core/sandbox/modules/import/lib/detect/find_file.lua2
-rw-r--r--xmake/core/sandbox/modules/import/lib/detect/find_path.lua2
-rw-r--r--xmake/core/sandbox/modules/import/lib/detect/find_program.lua4
-rw-r--r--xmake/core/sandbox/modules/import/lib/detect/find_programver.lua4
-rw-r--r--xmake/core/sandbox/modules/import/lib/lua/error.lua36
-rw-r--r--xmake/core/sandbox/modules/import/lib/lua/getmetatable.lua32
-rw-r--r--xmake/core/sandbox/modules/import/lib/lua/next.lua36
-rw-r--r--xmake/core/sandbox/modules/import/lib/lua/pcall.lua36
-rw-r--r--xmake/core/sandbox/modules/import/lib/lua/rawequal.lua33
-rw-r--r--xmake/core/sandbox/modules/import/lib/lua/rawget.lua34
-rw-r--r--xmake/core/sandbox/modules/import/lib/lua/rawset.lua33
-rw-r--r--xmake/core/sandbox/modules/import/lib/lua/select.lua36
-rw-r--r--xmake/core/sandbox/modules/import/lib/lua/setmetatable.lua36
-rw-r--r--xmake/core/sandbox/sandbox.lua95
-rw-r--r--xmake/core/tool/compiler.lua4
-rw-r--r--xmake/core/tool/linker.lua2
-rw-r--r--xmake/core/tool/tool.lua6
-rw-r--r--xmake/core/tool/toolchain.lua64
-rw-r--r--xmake/languages/c++/xmake.lua12
-rw-r--r--xmake/languages/c/load.lua1
-rw-r--r--xmake/modules/cli/binutils/bin2obj.lua3
-rw-r--r--xmake/modules/core/project/depend.lua3
-rw-r--r--xmake/modules/core/tools/clang.lua45
-rw-r--r--xmake/modules/core/tools/ml.lua20
-rw-r--r--xmake/modules/detect/sdks/find_mingw.lua3
-rw-r--r--xmake/modules/detect/sdks/find_ndk.lua2
-rw-r--r--xmake/modules/net/http/download.lua73
-rw-r--r--xmake/modules/package/manager/install_package.lua2
-rw-r--r--xmake/modules/package/manager/pkg/install_package.lua62
-rw-r--r--xmake/modules/package/manager/vcpkg/find_package.lua36
-rw-r--r--xmake/modules/package/manager/vcpkg/install_package.lua5
-rw-r--r--xmake/modules/package/manager/vcpkg/utils.lua18
-rw-r--r--xmake/modules/package/manager/xmake/search_package.lua4
-rw-r--r--xmake/modules/package/tools/cmake.lua34
-rw-r--r--xmake/modules/package/tools/xmake.lua23
-rw-r--r--xmake/modules/private/action/addon/impl/install_addons.lua142
-rw-r--r--xmake/modules/private/action/addon/impl/xrepo.lua57
-rw-r--r--xmake/modules/private/action/build/pcheader.lua16
-rw-r--r--xmake/modules/private/action/require/impl/actions/download.lua1
-rw-r--r--xmake/modules/private/action/require/impl/actions/download_resources.lua1
-rw-r--r--xmake/modules/private/action/require/impl/actions/install.lua57
-rw-r--r--xmake/modules/private/action/require/impl/actions/patch_sources.lua1
-rw-r--r--xmake/modules/private/action/require/impl/install_packages.lua25
-rw-r--r--xmake/modules/private/action/require/impl/package.lua148
-rw-r--r--xmake/modules/private/action/require/impl/repository.lua8
-rw-r--r--xmake/modules/private/action/require/install.lua9
-rw-r--r--xmake/modules/private/action/require/search.lua8
-rw-r--r--xmake/modules/private/check/checkers/api/package/kind.lua4
-rw-r--r--xmake/modules/private/utils/batchcmds.lua30
-rw-r--r--xmake/modules/private/utils/toolchain.lua54
-rw-r--r--xmake/modules/private/xrepo/action/install.lua9
-rw-r--r--xmake/modules/private/xrepo/action/remove.lua18
-rw-r--r--xmake/modules/private/xrepo/action/search.lua6
-rw-r--r--xmake/modules/private/xrepo/quick_search/cache.lua68
-rw-r--r--xmake/platforms/bsd/xmake.lua4
-rw-r--r--xmake/platforms/cross/xmake.lua4
-rw-r--r--xmake/platforms/linux/xmake.lua2
-rw-r--r--xmake/platforms/solaris/xmake.lua4
-rw-r--r--xmake/plugins/doxygen/main.lua4
-rw-r--r--xmake/plugins/format/main.lua4
-rw-r--r--xmake/plugins/macro/main.lua4
-rw-r--r--xmake/plugins/pack/wix/main.lua2
-rw-r--r--xmake/plugins/plugin/main.lua315
-rw-r--r--xmake/plugins/plugin/xmake.lua17
-rw-r--r--xmake/plugins/project/clang/compile_commands.lua11
-rw-r--r--xmake/plugins/project/ninja/build_ninja.lua3
-rw-r--r--xmake/plugins/project/utils/target_cmds.lua3
-rw-r--r--xmake/rules/c++/modules/builder.lua68
-rw-r--r--xmake/rules/c++/modules/clang/builder.lua8
-rw-r--r--xmake/rules/c++/modules/clang/scanner.lua5
-rw-r--r--xmake/rules/c++/modules/clang/support.lua79
-rw-r--r--xmake/rules/c++/modules/gcc/support.lua7
-rw-r--r--xmake/rules/c++/modules/msvc/support.lua7
-rw-r--r--xmake/rules/c++/modules/support.lua19
-rw-r--r--xmake/rules/utils/bin2c/utils.lua12
-rw-r--r--xmake/rules/utils/bin2c/xmake.lua2
-rw-r--r--xmake/rules/utils/bin2obj/utils.lua38
-rw-r--r--xmake/rules/utils/bin2obj/xmake.lua8
-rw-r--r--xmake/rules/utils/glsl2spv/xmake.lua3
-rw-r--r--xmake/rules/utils/hlsl2spv/xmake.lua3
-rw-r--r--xmake/templates/c++/console/src/main.cpp (renamed from xmake/repository/templates/c++/console/src/main.cpp)0
-rw-r--r--xmake/templates/c++/console/xmake.lua (renamed from xmake/repository/templates/c++/console/xmake.lua)0
-rw-r--r--xmake/templates/c++/module/binary/.gitignore (renamed from xmake/repository/templates/c++/module/binary/.gitignore)0
-rw-r--r--xmake/templates/c++/module/binary/modules/binary/bar/.gitignore (renamed from xmake/repository/templates/c++/module/binary/modules/binary/bar/.gitignore)0
-rw-r--r--xmake/templates/c++/module/binary/modules/binary/bar/src/add.cpp (renamed from xmake/repository/templates/c++/module/binary/modules/binary/bar/src/add.cpp)0
-rw-r--r--xmake/templates/c++/module/binary/modules/binary/bar/src/sub.cpp (renamed from xmake/repository/templates/c++/module/binary/modules/binary/bar/src/sub.cpp)0
-rw-r--r--xmake/templates/c++/module/binary/modules/binary/bar/xmake.lua (renamed from xmake/repository/templates/c++/module/binary/modules/binary/bar/xmake.lua)0
-rw-r--r--xmake/templates/c++/module/binary/src/main.cpp (renamed from xmake/repository/templates/c++/module/binary/src/main.cpp)0
-rw-r--r--xmake/templates/c++/module/binary/xmake.lua (renamed from xmake/repository/templates/c++/module/binary/xmake.lua)0
-rw-r--r--xmake/templates/c++/module/shared/.gitignore (renamed from xmake/repository/templates/c++/module/shared/.gitignore)0
-rw-r--r--xmake/templates/c++/module/shared/modules/shared/foo/src/foo.cpp (renamed from xmake/repository/templates/c++/module/shared/modules/shared/foo/src/foo.cpp)0
-rw-r--r--xmake/templates/c++/module/shared/modules/shared/foo/xmake.lua (renamed from xmake/repository/templates/c++/module/shared/modules/shared/foo/xmake.lua)0
-rw-r--r--xmake/templates/c++/module/shared/src/main.cpp (renamed from xmake/repository/templates/c++/module/shared/src/main.cpp)0
-rw-r--r--xmake/templates/c++/module/shared/xmake.lua (renamed from xmake/repository/templates/c++/module/shared/xmake.lua)0
-rw-r--r--xmake/templates/c++/qt/console/src/main.cpp (renamed from xmake/repository/templates/c++/qt/console/src/main.cpp)0
-rw-r--r--xmake/templates/c++/qt/console/xmake.lua (renamed from xmake/repository/templates/c++/qt/console/xmake.lua)0
-rw-r--r--xmake/templates/c++/qt/quickapp/src/main.cpp (renamed from xmake/repository/templates/c++/qt/quickapp/src/main.cpp)0
-rw-r--r--xmake/templates/c++/qt/quickapp/src/main.qml (renamed from xmake/repository/templates/c++/qt/quickapp/src/main.qml)0
-rw-r--r--xmake/templates/c++/qt/quickapp/src/qml.qrc (renamed from xmake/repository/templates/c++/qt/quickapp/src/qml.qrc)0
-rw-r--r--xmake/templates/c++/qt/quickapp/xmake.lua (renamed from xmake/repository/templates/c++/qt/quickapp/xmake.lua)0
-rw-r--r--xmake/templates/c++/qt/quickapp_static/src/main.cpp (renamed from xmake/repository/templates/c++/qt/quickapp_static/src/main.cpp)0
-rw-r--r--xmake/templates/c++/qt/quickapp_static/src/main.qml (renamed from xmake/repository/templates/c++/qt/quickapp_static/src/main.qml)0
-rw-r--r--xmake/templates/c++/qt/quickapp_static/src/qml.qrc (renamed from xmake/repository/templates/c++/qt/quickapp_static/src/qml.qrc)0
-rw-r--r--xmake/templates/c++/qt/quickapp_static/xmake.lua (renamed from xmake/repository/templates/c++/qt/quickapp_static/xmake.lua)0
-rw-r--r--xmake/templates/c++/qt/shared/src/demo.cpp (renamed from xmake/repository/templates/c++/qt/shared/src/demo.cpp)0
-rw-r--r--xmake/templates/c++/qt/shared/src/demo.h (renamed from xmake/repository/templates/c++/qt/shared/src/demo.h)0
-rw-r--r--xmake/templates/c++/qt/shared/src/demo_global.h (renamed from xmake/repository/templates/c++/qt/shared/src/demo_global.h)0
-rw-r--r--xmake/templates/c++/qt/shared/xmake.lua (renamed from xmake/repository/templates/c++/qt/shared/xmake.lua)0
-rw-r--r--xmake/templates/c++/qt/static/src/demo.cpp (renamed from xmake/repository/templates/c++/qt/static/src/demo.cpp)0
-rw-r--r--xmake/templates/c++/qt/static/src/demo.h (renamed from xmake/repository/templates/c++/qt/static/src/demo.h)0
-rw-r--r--xmake/templates/c++/qt/static/xmake.lua (renamed from xmake/repository/templates/c++/qt/static/xmake.lua)0
-rw-r--r--xmake/templates/c++/qt/widgetapp/src/main.cpp (renamed from xmake/repository/templates/c++/qt/widgetapp/src/main.cpp)0
-rw-r--r--xmake/templates/c++/qt/widgetapp/src/mainwindow.cpp (renamed from xmake/repository/templates/c++/qt/widgetapp/src/mainwindow.cpp)0
-rw-r--r--xmake/templates/c++/qt/widgetapp/src/mainwindow.h (renamed from xmake/repository/templates/c++/qt/widgetapp/src/mainwindow.h)0
-rw-r--r--xmake/templates/c++/qt/widgetapp/src/mainwindow.ui (renamed from xmake/repository/templates/c++/qt/widgetapp/src/mainwindow.ui)0
-rw-r--r--xmake/templates/c++/qt/widgetapp/xmake.lua (renamed from xmake/repository/templates/c++/qt/widgetapp/xmake.lua)0
-rw-r--r--xmake/templates/c++/qt/widgetapp_static/src/main.cpp (renamed from xmake/repository/templates/c++/qt/widgetapp_static/src/main.cpp)0
-rw-r--r--xmake/templates/c++/qt/widgetapp_static/src/mainwindow.cpp (renamed from xmake/repository/templates/c++/qt/widgetapp_static/src/mainwindow.cpp)0
-rw-r--r--xmake/templates/c++/qt/widgetapp_static/src/mainwindow.h (renamed from xmake/repository/templates/c++/qt/widgetapp_static/src/mainwindow.h)0
-rw-r--r--xmake/templates/c++/qt/widgetapp_static/src/mainwindow.ui (renamed from xmake/repository/templates/c++/qt/widgetapp_static/src/mainwindow.ui)0
-rw-r--r--xmake/templates/c++/qt/widgetapp_static/xmake.lua (renamed from xmake/repository/templates/c++/qt/widgetapp_static/xmake.lua)0
-rw-r--r--xmake/templates/c++/shared/src/foo.cpp (renamed from xmake/repository/templates/c++/shared/src/foo.cpp)0
-rw-r--r--xmake/templates/c++/shared/src/foo.h (renamed from xmake/repository/templates/c++/shared/src/foo.h)0
-rw-r--r--xmake/templates/c++/shared/src/main.cpp (renamed from xmake/repository/templates/c++/shared/src/main.cpp)0
-rw-r--r--xmake/templates/c++/shared/xmake.lua (renamed from xmake/repository/templates/c++/shared/xmake.lua)0
-rw-r--r--xmake/templates/c++/static/src/foo.cpp (renamed from xmake/repository/templates/c++/static/src/foo.cpp)0
-rw-r--r--xmake/templates/c++/static/src/foo.h (renamed from xmake/repository/templates/c++/static/src/foo.h)0
-rw-r--r--xmake/templates/c++/static/src/main.cpp (renamed from xmake/repository/templates/c++/static/src/main.cpp)0
-rw-r--r--xmake/templates/c++/static/xmake.lua (renamed from xmake/repository/templates/c++/static/xmake.lua)0
-rw-r--r--xmake/templates/c++/wxwidgets/src/main.cpp (renamed from xmake/repository/templates/c++/wxwidgets/src/main.cpp)0
-rw-r--r--xmake/templates/c++/wxwidgets/xmake.lua (renamed from xmake/repository/templates/c++/wxwidgets/xmake.lua)0
-rw-r--r--xmake/templates/c++/xmake/cli/src/lni/main.cpp (renamed from xmake/repository/templates/c++/xmake/cli/src/lni/main.cpp)0
-rw-r--r--xmake/templates/c++/xmake/cli/src/lua/main.lua (renamed from xmake/repository/templates/c++/xmake/cli/src/lua/main.lua)0
-rw-r--r--xmake/templates/c++/xmake/cli/xmake.lua (renamed from xmake/repository/templates/c++/xmake/cli/xmake.lua)0
-rw-r--r--xmake/templates/c/console/src/main.c (renamed from xmake/repository/templates/c/console/src/main.c)0
-rw-r--r--xmake/templates/c/console/xmake.lua (renamed from xmake/repository/templates/c/console/xmake.lua)0
-rw-r--r--xmake/templates/c/module/binary/.gitignore (renamed from xmake/repository/templates/c/module/binary/.gitignore)0
-rw-r--r--xmake/templates/c/module/binary/modules/binary/bar/.gitignore (renamed from xmake/repository/templates/c/module/binary/modules/binary/bar/.gitignore)0
-rw-r--r--xmake/templates/c/module/binary/modules/binary/bar/src/add.c (renamed from xmake/repository/templates/c/module/binary/modules/binary/bar/src/add.c)0
-rw-r--r--xmake/templates/c/module/binary/modules/binary/bar/src/sub.c (renamed from xmake/repository/templates/c/module/binary/modules/binary/bar/src/sub.c)0
-rw-r--r--xmake/templates/c/module/binary/modules/binary/bar/xmake.lua (renamed from xmake/repository/templates/c/module/binary/modules/binary/bar/xmake.lua)0
-rw-r--r--xmake/templates/c/module/binary/src/main.c (renamed from xmake/repository/templates/c/module/binary/src/main.c)0
-rw-r--r--xmake/templates/c/module/binary/xmake.lua (renamed from xmake/repository/templates/c/module/binary/xmake.lua)0
-rw-r--r--xmake/templates/c/module/shared/.gitignore (renamed from xmake/repository/templates/c/module/shared/.gitignore)0
-rw-r--r--xmake/templates/c/module/shared/modules/shared/foo/src/foo.c (renamed from xmake/repository/templates/c/module/shared/modules/shared/foo/src/foo.c)0
-rw-r--r--xmake/templates/c/module/shared/modules/shared/foo/xmake.lua (renamed from xmake/repository/templates/c/module/shared/modules/shared/foo/xmake.lua)0
-rw-r--r--xmake/templates/c/module/shared/src/main.c (renamed from xmake/repository/templates/c/module/shared/src/main.c)0
-rw-r--r--xmake/templates/c/module/shared/xmake.lua (renamed from xmake/repository/templates/c/module/shared/xmake.lua)0
-rw-r--r--xmake/templates/c/shared/src/foo.c (renamed from xmake/repository/templates/c/shared/src/foo.c)0
-rw-r--r--xmake/templates/c/shared/src/foo.h (renamed from xmake/repository/templates/c/shared/src/foo.h)0
-rw-r--r--xmake/templates/c/shared/src/main.c (renamed from xmake/repository/templates/c/shared/src/main.c)0
-rw-r--r--xmake/templates/c/shared/xmake.lua (renamed from xmake/repository/templates/c/shared/xmake.lua)0
-rw-r--r--xmake/templates/c/static/src/foo.c (renamed from xmake/repository/templates/c/static/src/foo.c)0
-rw-r--r--xmake/templates/c/static/src/foo.h (renamed from xmake/repository/templates/c/static/src/foo.h)0
-rw-r--r--xmake/templates/c/static/src/main.c (renamed from xmake/repository/templates/c/static/src/main.c)0
-rw-r--r--xmake/templates/c/static/xmake.lua (renamed from xmake/repository/templates/c/static/xmake.lua)0
-rw-r--r--xmake/templates/c/xmake/cli/src/lni/main.c (renamed from xmake/repository/templates/c/xmake/cli/src/lni/main.c)0
-rw-r--r--xmake/templates/c/xmake/cli/src/lua/main.lua (renamed from xmake/repository/templates/c/xmake/cli/src/lua/main.lua)0
-rw-r--r--xmake/templates/c/xmake/cli/xmake.lua (renamed from xmake/repository/templates/c/xmake/cli/xmake.lua)0
-rw-r--r--xmake/templates/csharp/console/src/Program.cs (renamed from xmake/repository/templates/csharp/console/src/Program.cs)0
-rw-r--r--xmake/templates/csharp/console/xmake.lua (renamed from xmake/repository/templates/csharp/console/xmake.lua)0
-rw-r--r--xmake/templates/csharp/shared/src/foo.cs (renamed from xmake/repository/templates/csharp/shared/src/foo.cs)0
-rw-r--r--xmake/templates/csharp/shared/src/main.cs (renamed from xmake/repository/templates/csharp/shared/src/main.cs)0
-rw-r--r--xmake/templates/csharp/shared/xmake.lua (renamed from xmake/repository/templates/csharp/shared/xmake.lua)0
-rw-r--r--xmake/templates/csharp/static/src/foo.cs (renamed from xmake/repository/templates/csharp/static/src/foo.cs)0
-rw-r--r--xmake/templates/csharp/static/src/main.cs (renamed from xmake/repository/templates/csharp/static/src/main.cs)0
-rw-r--r--xmake/templates/csharp/static/xmake.lua (renamed from xmake/repository/templates/csharp/static/xmake.lua)0
-rw-r--r--xmake/templates/cuda/console/src/main.cu (renamed from xmake/repository/templates/cuda/console/src/main.cu)0
-rw-r--r--xmake/templates/cuda/console/xmake.lua (renamed from xmake/repository/templates/cuda/console/xmake.lua)0
-rw-r--r--xmake/templates/cuda/shared/inc/lib.cuh (renamed from xmake/repository/templates/cuda/shared/inc/lib.cuh)0
-rw-r--r--xmake/templates/cuda/shared/src/lib.cu (renamed from xmake/repository/templates/cuda/shared/src/lib.cu)0
-rw-r--r--xmake/templates/cuda/shared/xmake.lua (renamed from xmake/repository/templates/cuda/shared/xmake.lua)0
-rw-r--r--xmake/templates/cuda/static/inc/lib.cuh (renamed from xmake/repository/templates/cuda/static/inc/lib.cuh)0
-rw-r--r--xmake/templates/cuda/static/src/lib.cu (renamed from xmake/repository/templates/cuda/static/src/lib.cu)0
-rw-r--r--xmake/templates/cuda/static/xmake.lua (renamed from xmake/repository/templates/cuda/static/xmake.lua)0
-rw-r--r--xmake/templates/dlang/console/src/main.d (renamed from xmake/repository/templates/dlang/console/src/main.d)0
-rw-r--r--xmake/templates/dlang/console/xmake.lua (renamed from xmake/repository/templates/dlang/console/xmake.lua)0
-rw-r--r--xmake/templates/dlang/shared/src/interfaces.d (renamed from xmake/repository/templates/dlang/shared/src/interfaces.d)0
-rw-r--r--xmake/templates/dlang/shared/src/main.d (renamed from xmake/repository/templates/dlang/shared/src/main.d)0
-rw-r--r--xmake/templates/dlang/shared/xmake.lua (renamed from xmake/repository/templates/dlang/shared/xmake.lua)0
-rw-r--r--xmake/templates/dlang/static/src/interfaces.d (renamed from xmake/repository/templates/dlang/static/src/interfaces.d)0
-rw-r--r--xmake/templates/dlang/static/src/main.d (renamed from xmake/repository/templates/dlang/static/src/main.d)0
-rw-r--r--xmake/templates/dlang/static/xmake.lua (renamed from xmake/repository/templates/dlang/static/xmake.lua)0
-rw-r--r--xmake/templates/fortran/console/src/main.f90 (renamed from xmake/repository/templates/fortran/console/src/main.f90)0
-rw-r--r--xmake/templates/fortran/console/xmake.lua (renamed from xmake/repository/templates/fortran/console/xmake.lua)0
-rw-r--r--xmake/templates/fortran/shared/src/main.f90 (renamed from xmake/repository/templates/fortran/shared/src/main.f90)0
-rw-r--r--xmake/templates/fortran/shared/src/test.f90 (renamed from xmake/repository/templates/fortran/shared/src/test.f90)0
-rw-r--r--xmake/templates/fortran/shared/xmake.lua (renamed from xmake/repository/templates/fortran/shared/xmake.lua)0
-rw-r--r--xmake/templates/fortran/static/src/main.f90 (renamed from xmake/repository/templates/fortran/static/src/main.f90)0
-rw-r--r--xmake/templates/fortran/static/src/test.f90 (renamed from xmake/repository/templates/fortran/static/src/test.f90)0
-rw-r--r--xmake/templates/fortran/static/xmake.lua (renamed from xmake/repository/templates/fortran/static/xmake.lua)0
-rw-r--r--xmake/templates/go/console/src/main.go (renamed from xmake/repository/templates/go/console/src/main.go)0
-rw-r--r--xmake/templates/go/console/xmake.lua (renamed from xmake/repository/templates/go/console/xmake.lua)0
-rw-r--r--xmake/templates/go/static/src/main.go (renamed from xmake/repository/templates/go/static/src/main.go)0
-rw-r--r--xmake/templates/go/static/src/module/add.go (renamed from xmake/repository/templates/go/static/src/module/add.go)0
-rw-r--r--xmake/templates/go/static/src/module/sub.go (renamed from xmake/repository/templates/go/static/src/module/sub.go)0
-rw-r--r--xmake/templates/go/static/src/test.go (renamed from xmake/repository/templates/go/static/src/test.go)0
-rw-r--r--xmake/templates/go/static/xmake.lua (renamed from xmake/repository/templates/go/static/xmake.lua)0
-rw-r--r--xmake/templates/kotlin/console/src/main.kt (renamed from xmake/repository/templates/kotlin/console/src/main.kt)0
-rw-r--r--xmake/templates/kotlin/console/xmake.lua (renamed from xmake/repository/templates/kotlin/console/xmake.lua)0
-rw-r--r--xmake/templates/kotlin/shared/src/foo.kt (renamed from xmake/repository/templates/kotlin/shared/src/foo.kt)0
-rw-r--r--xmake/templates/kotlin/shared/src/main.c (renamed from xmake/repository/templates/kotlin/shared/src/main.c)0
-rw-r--r--xmake/templates/kotlin/shared/xmake.lua (renamed from xmake/repository/templates/kotlin/shared/xmake.lua)0
-rw-r--r--xmake/templates/kotlin/static/src/foo.kt (renamed from xmake/repository/templates/kotlin/static/src/foo.kt)0
-rw-r--r--xmake/templates/kotlin/static/src/main.c (renamed from xmake/repository/templates/kotlin/static/src/main.c)0
-rw-r--r--xmake/templates/kotlin/static/xmake.lua (renamed from xmake/repository/templates/kotlin/static/xmake.lua)0
-rw-r--r--xmake/templates/nim/console/src/main.nim (renamed from xmake/repository/templates/nim/console/src/main.nim)0
-rw-r--r--xmake/templates/nim/console/xmake.lua (renamed from xmake/repository/templates/nim/console/xmake.lua)0
-rw-r--r--xmake/templates/nim/shared/src/foo.nim (renamed from xmake/repository/templates/nim/shared/src/foo.nim)0
-rw-r--r--xmake/templates/nim/shared/src/main.nim (renamed from xmake/repository/templates/nim/shared/src/main.nim)0
-rw-r--r--xmake/templates/nim/shared/xmake.lua (renamed from xmake/repository/templates/nim/shared/xmake.lua)0
-rw-r--r--xmake/templates/nim/static/src/foo.nim (renamed from xmake/repository/templates/nim/static/src/foo.nim)0
-rw-r--r--xmake/templates/nim/static/src/main.nim (renamed from xmake/repository/templates/nim/static/src/main.nim)0
-rw-r--r--xmake/templates/nim/static/xmake.lua (renamed from xmake/repository/templates/nim/static/xmake.lua)0
-rw-r--r--xmake/templates/objc++/console/src/main.mm (renamed from xmake/repository/templates/objc++/console/src/main.mm)0
-rw-r--r--xmake/templates/objc++/console/xmake.lua (renamed from xmake/repository/templates/objc++/console/xmake.lua)0
-rw-r--r--xmake/templates/objc++/xcode/bundle/src/Info.plist (renamed from xmake/repository/templates/objc++/xcode/bundle/src/Info.plist)0
-rw-r--r--xmake/templates/objc++/xcode/bundle/src/test.h (renamed from xmake/repository/templates/objc++/xcode/bundle/src/test.h)0
-rw-r--r--xmake/templates/objc++/xcode/bundle/src/test.mm (renamed from xmake/repository/templates/objc++/xcode/bundle/src/test.mm)0
-rw-r--r--xmake/templates/objc++/xcode/bundle/xmake.lua (renamed from xmake/repository/templates/objc++/xcode/bundle/xmake.lua)0
-rw-r--r--xmake/templates/objc++/xcode/framework/src/Info.plist (renamed from xmake/repository/templates/objc++/xcode/framework/src/Info.plist)0
-rw-r--r--xmake/templates/objc++/xcode/framework/src/test.h (renamed from xmake/repository/templates/objc++/xcode/framework/src/test.h)0
-rw-r--r--xmake/templates/objc++/xcode/framework/src/test.mm (renamed from xmake/repository/templates/objc++/xcode/framework/src/test.mm)0
-rw-r--r--xmake/templates/objc++/xcode/framework/xmake.lua (renamed from xmake/repository/templates/objc++/xcode/framework/xmake.lua)0
-rw-r--r--xmake/templates/objc/console/src/main.m (renamed from xmake/repository/templates/objc/console/src/main.m)0
-rw-r--r--xmake/templates/objc/console/xmake.lua (renamed from xmake/repository/templates/objc/console/xmake.lua)0
-rw-r--r--xmake/templates/objc/xcode/bundle/src/Info.plist (renamed from xmake/repository/templates/objc/xcode/bundle/src/Info.plist)0
-rw-r--r--xmake/templates/objc/xcode/bundle/src/test.h (renamed from xmake/repository/templates/objc/xcode/bundle/src/test.h)0
-rw-r--r--xmake/templates/objc/xcode/bundle/src/test.m (renamed from xmake/repository/templates/objc/xcode/bundle/src/test.m)0
-rw-r--r--xmake/templates/objc/xcode/bundle/xmake.lua (renamed from xmake/repository/templates/objc/xcode/bundle/xmake.lua)0
-rw-r--r--xmake/templates/objc/xcode/framework/src/Info.plist (renamed from xmake/repository/templates/objc/xcode/framework/src/Info.plist)0
-rw-r--r--xmake/templates/objc/xcode/framework/src/test.h (renamed from xmake/repository/templates/objc/xcode/framework/src/test.h)0
-rw-r--r--xmake/templates/objc/xcode/framework/src/test.m (renamed from xmake/repository/templates/objc/xcode/framework/src/test.m)0
-rw-r--r--xmake/templates/objc/xcode/framework/xmake.lua (renamed from xmake/repository/templates/objc/xcode/framework/xmake.lua)0
-rw-r--r--xmake/templates/objc/xcode/iosapp/src/AppDelegate.h (renamed from xmake/repository/templates/objc/xcode/iosapp/src/AppDelegate.h)0
-rw-r--r--xmake/templates/objc/xcode/iosapp/src/AppDelegate.m (renamed from xmake/repository/templates/objc/xcode/iosapp/src/AppDelegate.m)0
-rw-r--r--xmake/templates/objc/xcode/iosapp/src/Assets.xcassets/AppIcon.appiconset/Contents.json (renamed from xmake/repository/templates/objc/xcode/iosapp/src/Assets.xcassets/AppIcon.appiconset/Contents.json)0
-rw-r--r--xmake/templates/objc/xcode/iosapp/src/Assets.xcassets/Contents.json (renamed from xmake/repository/templates/objc/xcode/iosapp/src/Assets.xcassets/Contents.json)0
-rw-r--r--xmake/templates/objc/xcode/iosapp/src/Base.lproj/LaunchScreen.storyboard (renamed from xmake/repository/templates/objc/xcode/iosapp/src/Base.lproj/LaunchScreen.storyboard)0
-rw-r--r--xmake/templates/objc/xcode/iosapp/src/Base.lproj/Main.storyboard (renamed from xmake/repository/templates/objc/xcode/iosapp/src/Base.lproj/Main.storyboard)0
-rw-r--r--xmake/templates/objc/xcode/iosapp/src/Info.plist (renamed from xmake/repository/templates/objc/xcode/iosapp/src/Info.plist)0
-rw-r--r--xmake/templates/objc/xcode/iosapp/src/SceneDelegate.h (renamed from xmake/repository/templates/objc/xcode/iosapp/src/SceneDelegate.h)0
-rw-r--r--xmake/templates/objc/xcode/iosapp/src/SceneDelegate.m (renamed from xmake/repository/templates/objc/xcode/iosapp/src/SceneDelegate.m)0
-rw-r--r--xmake/templates/objc/xcode/iosapp/src/ViewController.h (renamed from xmake/repository/templates/objc/xcode/iosapp/src/ViewController.h)0
-rw-r--r--xmake/templates/objc/xcode/iosapp/src/ViewController.m (renamed from xmake/repository/templates/objc/xcode/iosapp/src/ViewController.m)0
-rw-r--r--xmake/templates/objc/xcode/iosapp/src/main.m (renamed from xmake/repository/templates/objc/xcode/iosapp/src/main.m)0
-rw-r--r--xmake/templates/objc/xcode/iosapp/xmake.lua (renamed from xmake/repository/templates/objc/xcode/iosapp/xmake.lua)0
-rw-r--r--xmake/templates/objc/xcode/iosapp_with_framework/src/app/AppDelegate.h (renamed from xmake/repository/templates/objc/xcode/iosapp_with_framework/src/app/AppDelegate.h)0
-rw-r--r--xmake/templates/objc/xcode/iosapp_with_framework/src/app/AppDelegate.m (renamed from xmake/repository/templates/objc/xcode/iosapp_with_framework/src/app/AppDelegate.m)0
-rw-r--r--xmake/templates/objc/xcode/iosapp_with_framework/src/app/Assets.xcassets/AppIcon.appiconset/Contents.json (renamed from xmake/repository/templates/objc/xcode/iosapp_with_framework/src/app/Assets.xcassets/AppIcon.appiconset/Contents.json)0
-rw-r--r--xmake/templates/objc/xcode/iosapp_with_framework/src/app/Assets.xcassets/Contents.json (renamed from xmake/repository/templates/objc/xcode/iosapp_with_framework/src/app/Assets.xcassets/Contents.json)0
-rw-r--r--xmake/templates/objc/xcode/iosapp_with_framework/src/app/Base.lproj/LaunchScreen.storyboard (renamed from xmake/repository/templates/objc/xcode/iosapp_with_framework/src/app/Base.lproj/LaunchScreen.storyboard)0
-rw-r--r--xmake/templates/objc/xcode/iosapp_with_framework/src/app/Base.lproj/Main.storyboard (renamed from xmake/repository/templates/objc/xcode/iosapp_with_framework/src/app/Base.lproj/Main.storyboard)0
-rw-r--r--xmake/templates/objc/xcode/iosapp_with_framework/src/app/Info.plist (renamed from xmake/repository/templates/objc/xcode/iosapp_with_framework/src/app/Info.plist)0
-rw-r--r--xmake/templates/objc/xcode/iosapp_with_framework/src/app/SceneDelegate.h (renamed from xmake/repository/templates/objc/xcode/iosapp_with_framework/src/app/SceneDelegate.h)0
-rw-r--r--xmake/templates/objc/xcode/iosapp_with_framework/src/app/SceneDelegate.m (renamed from xmake/repository/templates/objc/xcode/iosapp_with_framework/src/app/SceneDelegate.m)0
-rw-r--r--xmake/templates/objc/xcode/iosapp_with_framework/src/app/ViewController.h (renamed from xmake/repository/templates/objc/xcode/iosapp_with_framework/src/app/ViewController.h)0
-rw-r--r--xmake/templates/objc/xcode/iosapp_with_framework/src/app/ViewController.m (renamed from xmake/repository/templates/objc/xcode/iosapp_with_framework/src/app/ViewController.m)0
-rw-r--r--xmake/templates/objc/xcode/iosapp_with_framework/src/app/main.m (renamed from xmake/repository/templates/objc/xcode/iosapp_with_framework/src/app/main.m)0
-rw-r--r--xmake/templates/objc/xcode/iosapp_with_framework/src/framework/Info.plist (renamed from xmake/repository/templates/objc/xcode/iosapp_with_framework/src/framework/Info.plist)0
-rw-r--r--xmake/templates/objc/xcode/iosapp_with_framework/src/framework/test.h (renamed from xmake/repository/templates/objc/xcode/iosapp_with_framework/src/framework/test.h)0
-rw-r--r--xmake/templates/objc/xcode/iosapp_with_framework/src/framework/test.m (renamed from xmake/repository/templates/objc/xcode/iosapp_with_framework/src/framework/test.m)0
-rw-r--r--xmake/templates/objc/xcode/iosapp_with_framework/xmake.lua (renamed from xmake/repository/templates/objc/xcode/iosapp_with_framework/xmake.lua)0
-rw-r--r--xmake/templates/objc/xcode/macapp/src/AppDelegate.h (renamed from xmake/repository/templates/objc/xcode/macapp/src/AppDelegate.h)0
-rw-r--r--xmake/templates/objc/xcode/macapp/src/AppDelegate.m (renamed from xmake/repository/templates/objc/xcode/macapp/src/AppDelegate.m)0
-rw-r--r--xmake/templates/objc/xcode/macapp/src/Assets.xcassets/AppIcon.appiconset/Contents.json (renamed from xmake/repository/templates/objc/xcode/macapp/src/Assets.xcassets/AppIcon.appiconset/Contents.json)0
-rw-r--r--xmake/templates/objc/xcode/macapp/src/Assets.xcassets/Contents.json (renamed from xmake/repository/templates/objc/xcode/macapp/src/Assets.xcassets/Contents.json)0
-rw-r--r--xmake/templates/objc/xcode/macapp/src/Base.lproj/Main.storyboard (renamed from xmake/repository/templates/objc/xcode/macapp/src/Base.lproj/Main.storyboard)0
-rw-r--r--xmake/templates/objc/xcode/macapp/src/Info.plist (renamed from xmake/repository/templates/objc/xcode/macapp/src/Info.plist)0
-rw-r--r--xmake/templates/objc/xcode/macapp/src/ViewController.h (renamed from xmake/repository/templates/objc/xcode/macapp/src/ViewController.h)0
-rw-r--r--xmake/templates/objc/xcode/macapp/src/ViewController.m (renamed from xmake/repository/templates/objc/xcode/macapp/src/ViewController.m)0
-rw-r--r--xmake/templates/objc/xcode/macapp/src/main.m (renamed from xmake/repository/templates/objc/xcode/macapp/src/main.m)0
-rw-r--r--xmake/templates/objc/xcode/macapp/src/test.entitlements (renamed from xmake/repository/templates/objc/xcode/macapp/src/test.entitlements)0
-rw-r--r--xmake/templates/objc/xcode/macapp/xmake.lua (renamed from xmake/repository/templates/objc/xcode/macapp/xmake.lua)0
-rw-r--r--xmake/templates/objc/xcode/macapp_with_framework/src/app/AppDelegate.h (renamed from xmake/repository/templates/objc/xcode/macapp_with_framework/src/app/AppDelegate.h)0
-rw-r--r--xmake/templates/objc/xcode/macapp_with_framework/src/app/AppDelegate.m (renamed from xmake/repository/templates/objc/xcode/macapp_with_framework/src/app/AppDelegate.m)0
-rw-r--r--xmake/templates/objc/xcode/macapp_with_framework/src/app/Assets.xcassets/AppIcon.appiconset/Contents.json (renamed from xmake/repository/templates/objc/xcode/macapp_with_framework/src/app/Assets.xcassets/AppIcon.appiconset/Contents.json)0
-rw-r--r--xmake/templates/objc/xcode/macapp_with_framework/src/app/Assets.xcassets/Contents.json (renamed from xmake/repository/templates/objc/xcode/macapp_with_framework/src/app/Assets.xcassets/Contents.json)0
-rw-r--r--xmake/templates/objc/xcode/macapp_with_framework/src/app/Base.lproj/Main.storyboard (renamed from xmake/repository/templates/objc/xcode/macapp_with_framework/src/app/Base.lproj/Main.storyboard)0
-rw-r--r--xmake/templates/objc/xcode/macapp_with_framework/src/app/Info.plist (renamed from xmake/repository/templates/objc/xcode/macapp_with_framework/src/app/Info.plist)0
-rw-r--r--xmake/templates/objc/xcode/macapp_with_framework/src/app/ViewController.h (renamed from xmake/repository/templates/objc/xcode/macapp_with_framework/src/app/ViewController.h)0
-rw-r--r--xmake/templates/objc/xcode/macapp_with_framework/src/app/ViewController.m (renamed from xmake/repository/templates/objc/xcode/macapp_with_framework/src/app/ViewController.m)0
-rw-r--r--xmake/templates/objc/xcode/macapp_with_framework/src/app/main.m (renamed from xmake/repository/templates/objc/xcode/macapp_with_framework/src/app/main.m)0
-rw-r--r--xmake/templates/objc/xcode/macapp_with_framework/src/app/test.entitlements (renamed from xmake/repository/templates/objc/xcode/macapp_with_framework/src/app/test.entitlements)0
-rw-r--r--xmake/templates/objc/xcode/macapp_with_framework/src/framework/Info.plist (renamed from xmake/repository/templates/objc/xcode/macapp_with_framework/src/framework/Info.plist)0
-rw-r--r--xmake/templates/objc/xcode/macapp_with_framework/src/framework/test.h (renamed from xmake/repository/templates/objc/xcode/macapp_with_framework/src/framework/test.h)0
-rw-r--r--xmake/templates/objc/xcode/macapp_with_framework/src/framework/test.m (renamed from xmake/repository/templates/objc/xcode/macapp_with_framework/src/framework/test.m)0
-rw-r--r--xmake/templates/objc/xcode/macapp_with_framework/xmake.lua (renamed from xmake/repository/templates/objc/xcode/macapp_with_framework/xmake.lua)0
-rw-r--r--xmake/templates/pascal/console/src/main.pas (renamed from xmake/repository/templates/pascal/console/src/main.pas)0
-rw-r--r--xmake/templates/pascal/console/xmake.lua (renamed from xmake/repository/templates/pascal/console/xmake.lua)0
-rw-r--r--xmake/templates/pascal/shared/src/foo.pas (renamed from xmake/repository/templates/pascal/shared/src/foo.pas)0
-rw-r--r--xmake/templates/pascal/shared/src/main.pas (renamed from xmake/repository/templates/pascal/shared/src/main.pas)0
-rw-r--r--xmake/templates/pascal/shared/xmake.lua (renamed from xmake/repository/templates/pascal/shared/xmake.lua)0
-rw-r--r--xmake/templates/rust/console/src/main.rs (renamed from xmake/repository/templates/rust/console/src/main.rs)0
-rw-r--r--xmake/templates/rust/console/xmake.lua (renamed from xmake/repository/templates/rust/console/xmake.lua)0
-rw-r--r--xmake/templates/rust/shared/src/foo.rs (renamed from xmake/repository/templates/rust/shared/src/foo.rs)0
-rw-r--r--xmake/templates/rust/shared/src/main.rs (renamed from xmake/repository/templates/rust/shared/src/main.rs)0
-rw-r--r--xmake/templates/rust/shared/xmake.lua (renamed from xmake/repository/templates/rust/shared/xmake.lua)0
-rw-r--r--xmake/templates/rust/static/src/foo.rs (renamed from xmake/repository/templates/rust/static/src/foo.rs)0
-rw-r--r--xmake/templates/rust/static/src/main.rs (renamed from xmake/repository/templates/rust/static/src/main.rs)0
-rw-r--r--xmake/templates/rust/static/xmake.lua (renamed from xmake/repository/templates/rust/static/xmake.lua)0
-rw-r--r--xmake/templates/swift/console/src/main.swift (renamed from xmake/repository/templates/swift/console/src/main.swift)0
-rw-r--r--xmake/templates/swift/console/xmake.lua (renamed from xmake/repository/templates/swift/console/xmake.lua)0
-rw-r--r--xmake/templates/vala/console/src/main.vala (renamed from xmake/repository/templates/vala/console/src/main.vala)0
-rw-r--r--xmake/templates/vala/console/xmake.lua (renamed from xmake/repository/templates/vala/console/xmake.lua)0
-rw-r--r--xmake/templates/vala/shared/src/main.vala (renamed from xmake/repository/templates/vala/shared/src/main.vala)0
-rw-r--r--xmake/templates/vala/shared/src/mymath.vala (renamed from xmake/repository/templates/vala/shared/src/mymath.vala)0
-rw-r--r--xmake/templates/vala/shared/xmake.lua (renamed from xmake/repository/templates/vala/shared/xmake.lua)0
-rw-r--r--xmake/templates/vala/static/src/main.vala (renamed from xmake/repository/templates/vala/static/src/main.vala)0
-rw-r--r--xmake/templates/vala/static/src/mymath.vala (renamed from xmake/repository/templates/vala/static/src/mymath.vala)0
-rw-r--r--xmake/templates/vala/static/xmake.lua (renamed from xmake/repository/templates/vala/static/xmake.lua)0
-rw-r--r--xmake/templates/zig/console/src/main.zig (renamed from xmake/repository/templates/zig/console/src/main.zig)0
-rw-r--r--xmake/templates/zig/console/xmake.lua (renamed from xmake/repository/templates/zig/console/xmake.lua)0
-rw-r--r--xmake/templates/zig/shared/src/main.zig (renamed from xmake/repository/templates/zig/shared/src/main.zig)0
-rw-r--r--xmake/templates/zig/shared/src/test.zig (renamed from xmake/repository/templates/zig/shared/src/test.zig)0
-rw-r--r--xmake/templates/zig/shared/xmake.lua (renamed from xmake/repository/templates/zig/shared/xmake.lua)0
-rw-r--r--xmake/templates/zig/static/src/main.zig (renamed from xmake/repository/templates/zig/static/src/main.zig)0
-rw-r--r--xmake/templates/zig/static/src/test.zig (renamed from xmake/repository/templates/zig/static/src/test.zig)0
-rw-r--r--xmake/templates/zig/static/xmake.lua (renamed from xmake/repository/templates/zig/static/xmake.lua)0
-rw-r--r--xmake/toolchains/mingw/xmake.lua2
444 files changed, 6195 insertions, 595 deletions
diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml
index 2bb1b81ea..5bbc0f047 100644
--- a/.github/workflows/linux.yml
+++ b/.github/workflows/linux.yml
@@ -62,12 +62,11 @@ jobs:
sudo apt install -y dh-make rng-tools devscripts lintian
echo "$PPA_GPG_PRIKEY_2C0C68C9" > ppa_gpg.key
gpg --import ppa_gpg.key
- scripts/makeppa questing # 25.10
- scripts/makeppa plucky # 25.04
+ # @note only the series which are not obsolete on launchpad can be uploaded,
+ # @see https://api.launchpad.net/1.0/ubuntu/series
+ scripts/makeppa stonking # 26.10
+ scripts/makeppa resolute # 26.04
scripts/makeppa noble # 24.04
- scripts/makeppa mantic # 23.10
- scripts/makeppa lunar # 23.04
- scripts/makeppa kinetic # 22.10
scripts/makeppa jammy # 22.04
scripts/makeppa focal # 20.04
scripts/makeppa bionic # 18.04
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 616ce7947..52dc9bdc5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,112 @@
## master (unreleased)
+## v3.1.1
+
+### New features
+
+* [#7696](https://github.com/xmake-io/xmake/pull/7696): Add addons support, to extend xmake with plugins, rules, toolchains, templates, modules and includes files
+* [#7702](https://github.com/xmake-io/xmake/pull/7702): Auto-install the addons declared by a project and lock them in `xmake-addons.lock`
+* [#7714](https://github.com/xmake-io/xmake/pull/7714): Support `add_addons(...)` in `xmake.lua`
+* [#7706](https://github.com/xmake-io/xmake/pull/7706): Add tests for addons
+* [#7707](https://github.com/xmake-io/xmake/pull/7707): Add tests for the package definitions shipped by addons
+* [#7717](https://github.com/xmake-io/xmake/pull/7717): Move the `format` / `doxygen` / `macro` plugins to addons
+* [#7723](https://github.com/xmake-io/xmake/pull/7723): Add new cross-compilation architectures, e.g. SPARC64
+* [#7721](https://github.com/xmake-io/xmake/pull/7721): Add the `package.host.install_locally` policy
+* [#7699](https://github.com/xmake-io/xmake/pull/7699): Rework the templates directory and template distribution
+* Add the `pkg` package manager for BSD
+
+### Changes
+
+* [#7719](https://github.com/xmake-io/xmake/pull/7719): Propagate the vector extensions of a package to its consumers
+* [#7713](https://github.com/xmake-io/xmake/pull/7713): Run `hlsl2spv` / `glsl2spv` before the C++ module scan
+* [#7722](https://github.com/xmake-io/xmake/pull/7722): Improve the interpreter, project loading, search cache and semver
+* [#7733](https://github.com/xmake-io/xmake/pull/7733): Check the name conflicts of the global modules of addons
+* [#7726](https://github.com/xmake-io/xmake/pull/7726): Improve the mingw toolchain for clang and libc++
+
+### Bugs fixed
+
+* [#7737](https://github.com/xmake-io/xmake/pull/7737): Fix the duplicated packages of the nested builds when installing them locally
+* [#7738](https://github.com/xmake-io/xmake/issues/7738): Fix the platform menu of the remote build, e.g. `xmake f -p windows --wdk=xxx`
+* [#7710](https://github.com/xmake-io/xmake/pull/7710): Fix the link name of libraries ending with `.lib`
+* [#7709](https://github.com/xmake-io/xmake/pull/7709): Fix the runtime flags of clang
+* [#7703](https://github.com/xmake-io/xmake/pull/7703): Fix wix to recognize `x86_64` when passing `-arch`
+* [#7701](https://github.com/xmake-io/xmake/pull/7701): Fix the dependency check to detect appended nested values
+* [#7698](https://github.com/xmake-io/xmake/pull/7698): Fix the semver version selection and build metadata sorting
+* Fix `scheduler.co_resume` to raise the errors of the resumed coroutine
+
+## v3.1.0
+
+### New features
+
+* [#7558](https://github.com/xmake-io/xmake/pull/7558): Add `check_alignof` / `alignof` detection support
+* [#7587](https://github.com/xmake-io/xmake/pull/7587): Support `xmake show --format=json`
+* [#7607](https://github.com/xmake-io/xmake/pull/7607): Support passing multiple target names to `build` / `clean` and other commands
+* [#7634](https://github.com/xmake-io/xmake/pull/7634): Add filter support for xpack
+* [#7654](https://github.com/xmake-io/xmake/pull/7654): Add `batchcmds:call` and lua-file transform support for `bin2obj` / `bin2c`
+* [#7680](https://github.com/xmake-io/xmake/pull/7680): Rework `xmake plugin` to install plugins from repositories (`repo@name` or name), git urls (`github:user/repo[#branch]`) and local directories using the packages-style `plugins/<first-letter>/<name>` layout, and list built-in / installed / available plugins with their descriptions
+* Add gcc-16 toolchain support
+
+### Changes
+
+* [#7562](https://github.com/xmake-io/xmake/pull/7562): Improve nuget version handling
+* [#7564](https://github.com/xmake-io/xmake/pull/7564): Improve vcpkg dependency info in `find_package`
+* [#7582](https://github.com/xmake-io/xmake/pull/7582): Support `set_encodings` and filter output for nvcc
+* [#7609](https://github.com/xmake-io/xmake/pull/7609): Use `FormatMessageW` for Windows system error messages
+* [#7614](https://github.com/xmake-io/xmake/pull/7614): Improve `cargo` package installation
+* [#7619](https://github.com/xmake-io/xmake/pull/7619): Work around clangd drive-letter casing issue
+* [#7620](https://github.com/xmake-io/xmake/pull/7620): Improve cl flag detection and output handling on vs2015
+* [#7625](https://github.com/xmake-io/xmake/pull/7625): Improve verilator rules
+* [#7629](https://github.com/xmake-io/xmake/pull/7629): vsxmake: support custom source types in the solution explorer and nonetype targets
+* [#7630](https://github.com/xmake-io/xmake/pull/7630): Add `add_toolset` api checker
+* [#7637](https://github.com/xmake-io/xmake/pull/7637): Improve readline / curses options for mingw
+* [#7648](https://github.com/xmake-io/xmake/pull/7648): Improve error tips
+* [#7655](https://github.com/xmake-io/xmake/pull/7655): Improve bin2obj object-flag detection and add ppc / mips support
+* [#7657](https://github.com/xmake-io/xmake/pull/7657): Update NDK sdkver for riscv64
+* [#7666](https://github.com/xmake-io/xmake/pull/7666): Improve vcpkg package discovery
+* [#7672](https://github.com/xmake-io/xmake/pull/7672): Add fallback download support
+* [#7688](https://github.com/xmake-io/xmake/pull/7688): Improve static libc++ runtime links for clang
+* [#7689](https://github.com/xmake-io/xmake/pull/7689): Rewrite `xmake plugin --install` to install plugins from xmake-repo as `plugin` kind packages, reusing the package installation flow
+* [#7693](https://github.com/xmake-io/xmake/pull/7693): Replace the deprecated tbox interfaces
+* Improve elf rpath cleaning and package path handling
+* Add xcodebuild detection
+* Update the bundled tbox
+
+### Bugs fixed
+
+* [#7561](https://github.com/xmake-io/xmake/pull/7561): Fix `-flto=thin` regression for clang-cl
+* [#7563](https://github.com/xmake-io/xmake/pull/7563): Fix crash in `contains` and improve PCH for C++ std modules
+* [#7580](https://github.com/xmake-io/xmake/pull/7580): Fix the format plugin when the header path is already absolute
+* [#7581](https://github.com/xmake-io/xmake/pull/7581): Fix missing paren in package loading
+* [#7583](https://github.com/xmake-io/xmake/pull/7583): Fix splitting global flags for cmake packages
+* [#7599](https://github.com/xmake-io/xmake/pull/7599): Fix gzip / tar detection on OpenBSD
+* [#7600](https://github.com/xmake-io/xmake/pull/7600): Fix enabling the build cache memcache
+* [#7601](https://github.com/xmake-io/xmake/pull/7601): Fix verilator to always define `TRACE`
+* [#7602](https://github.com/xmake-io/xmake/pull/7602): Fix package load memcache
+* [#7603](https://github.com/xmake-io/xmake/pull/7603): Fix `rmdir` via tbox update
+* [#7604](https://github.com/xmake-io/xmake/pull/7604): Fix rpath cleaning for elf
+* [#7608](https://github.com/xmake-io/xmake/pull/7608): Fix typos and format command menus
+* [#7611](https://github.com/xmake-io/xmake/pull/7611): Fix cache environment variable handling ([#7576](https://github.com/xmake-io/xmake/issues/7576))
+* [#7622](https://github.com/xmake-io/xmake/pull/7622): Fix default maintainer for deb packaging
+* [#7627](https://github.com/xmake-io/xmake/pull/7627): Fix PATH handling in debuild
+* [#7631](https://github.com/xmake-io/xmake/pull/7631): Fix PATH handling when installing deb packages
+* [#7633](https://github.com/xmake-io/xmake/pull/7633): Fix pkg-config path for BSD in the Meson backend
+* [#7640](https://github.com/xmake-io/xmake/pull/7640): Improve emcc detection with a fallback search
+* [#7644](https://github.com/xmake-io/xmake/pull/7644): Fix stack buffer overflow for long paths in path translation
+* [#7645](https://github.com/xmake-io/xmake/pull/7645): Fix removing read-only directories
+* [#7659](https://github.com/xmake-io/xmake/pull/7659): Fix emcc detection for `.exe` / `.bat` on Windows
+* [#7661](https://github.com/xmake-io/xmake/pull/7661): Revert static libc++ link for clang
+* [#7668](https://github.com/xmake-io/xmake/pull/7668): Fix passing paths to cmake
+* [#7671](https://github.com/xmake-io/xmake/pull/7671): Fix tbox bugs
+* [#7674](https://github.com/xmake-io/xmake/pull/7674): Fix Mach host send-right leaks on macOS
+* [#7676](https://github.com/xmake-io/xmake/pull/7676): Fix masm symbol flags for embed / edit levels
+* [#7679](https://github.com/xmake-io/xmake/pull/7679): Fix `set_pcheader` for msvc in C mode
+* [#7684](https://github.com/xmake-io/xmake/pull/7684): Fix wrong Lua stack index in `process.open`
+* [#7685](https://github.com/xmake-io/xmake/pull/7685): Fix engine resource leaks
+* [#7687](https://github.com/xmake-io/xmake/pull/7687): Fix the ninja generator to emit build edges for `win.sdk.resource` source batches ([#7682](https://github.com/xmake-io/xmake/issues/7682))
+* [#7692](https://github.com/xmake-io/xmake/pull/7692): Fix dependency order for `build.c++.modules.tryreuse`
+* Fix trybuild for scons
+
## v3.0.9
### New features
@@ -2418,6 +2524,112 @@
## master (开发中)
+## v3.1.1
+
+### 新特性
+
+* [#7696](https://github.com/xmake-io/xmake/pull/7696): 添加 addons 支持,可以用插件、规则、工具链、模板、模块和 includes 文件扩展 xmake
+* [#7702](https://github.com/xmake-io/xmake/pull/7702): 自动安装工程声明的 addon,并用 `xmake-addons.lock` 锁定版本
+* [#7714](https://github.com/xmake-io/xmake/pull/7714): 支持在 `xmake.lua` 中使用 `add_addons(...)`
+* [#7706](https://github.com/xmake-io/xmake/pull/7706): 添加 addon 测试
+* [#7707](https://github.com/xmake-io/xmake/pull/7707): 添加 addon 分发 package 定义的测试
+* [#7717](https://github.com/xmake-io/xmake/pull/7717): 将 `format` / `doxygen` / `macro` 插件迁移为 addon
+* [#7723](https://github.com/xmake-io/xmake/pull/7723): 添加新的交叉编译架构,例如 SPARC64
+* [#7721](https://github.com/xmake-io/xmake/pull/7721): 添加 `package.host.install_locally` 策略
+* [#7699](https://github.com/xmake-io/xmake/pull/7699): 重构模板目录和模板分发
+* 添加 BSD 的 `pkg` 包管理器支持
+
+### 改进
+
+* [#7719](https://github.com/xmake-io/xmake/pull/7719): 将包的向量扩展配置传递给使用它的目标
+* [#7713](https://github.com/xmake-io/xmake/pull/7713): 让 `hlsl2spv` / `glsl2spv` 在 C++ 模块扫描之前执行
+* [#7722](https://github.com/xmake-io/xmake/pull/7722): 改进 interpreter、工程加载、搜索缓存和 semver
+* [#7733](https://github.com/xmake-io/xmake/pull/7733): 检查 addon 全局模块的命名冲突
+* [#7726](https://github.com/xmake-io/xmake/pull/7726): 改进 mingw 工具链对 clang 和 libc++ 的支持
+
+### Bug 修复
+
+* [#7737](https://github.com/xmake-io/xmake/pull/7737): 修复本地安装包时,嵌套构建重复安装包的问题
+* [#7738](https://github.com/xmake-io/xmake/issues/7738): 修复远程编译下的平台菜单,例如 `xmake f -p windows --wdk=xxx`
+* [#7710](https://github.com/xmake-io/xmake/pull/7710): 修复 `.lib` 结尾的库名被错误裁剪的问题
+* [#7709](https://github.com/xmake-io/xmake/pull/7709): 修复 clang 的 runtime flags 检测
+* [#7703](https://github.com/xmake-io/xmake/pull/7703): 修复 wix 传递 `-arch` 时无法识别 `x86_64` 的问题
+* [#7701](https://github.com/xmake-io/xmake/pull/7701): 修复依赖检测无法发现嵌套值追加的问题
+* [#7698](https://github.com/xmake-io/xmake/pull/7698): 修复 semver 的版本选择和 build metadata 排序
+* 修复 `scheduler.co_resume`,让被恢复协程的错误能够正常抛出
+
+## v3.1.0
+
+### 新特性
+
+* [#7558](https://github.com/xmake-io/xmake/pull/7558): 添加 `check_alignof` / `alignof` 检测支持
+* [#7587](https://github.com/xmake-io/xmake/pull/7587): 支持 `xmake show --format=json`
+* [#7607](https://github.com/xmake-io/xmake/pull/7607): 支持给 `build` / `clean` 等命令传递多个目标名
+* [#7634](https://github.com/xmake-io/xmake/pull/7634): 为 xpack 添加过滤器支持
+* [#7654](https://github.com/xmake-io/xmake/pull/7654): 为 `bin2obj` / `bin2c` 添加 `batchcmds:call` 和 lua 文件 transform 支持
+* [#7680](https://github.com/xmake-io/xmake/pull/7680): 重构 `xmake plugin`,支持从仓库(`repo@name` 或名称)、git 地址(`github:user/repo[#branch]`)以及本地目录安装插件,仓库中的插件采用与 packages 相同的 `plugins/<首字母>/<名称>` 布局,并列出内置、已安装和仓库中可安装的插件及其描述
+* 添加 gcc-16 工具链支持
+
+### 改进
+
+* [#7562](https://github.com/xmake-io/xmake/pull/7562): 改进 nuget 版本处理
+* [#7564](https://github.com/xmake-io/xmake/pull/7564): 改进 `find_package` 中的 vcpkg 依赖信息
+* [#7582](https://github.com/xmake-io/xmake/pull/7582): 支持 nvcc 的 `set_encodings` 并过滤其输出
+* [#7609](https://github.com/xmake-io/xmake/pull/7609): 使用 `FormatMessageW` 获取 Windows 系统错误信息
+* [#7614](https://github.com/xmake-io/xmake/pull/7614): 改进 `cargo` 包安装
+* [#7619](https://github.com/xmake-io/xmake/pull/7619): 规避 clangd 盘符大小写问题
+* [#7620](https://github.com/xmake-io/xmake/pull/7620): 改进 vs2015 下 cl 的编译选项检测和输出处理
+* [#7625](https://github.com/xmake-io/xmake/pull/7625): 改进 verilator 规则
+* [#7629](https://github.com/xmake-io/xmake/pull/7629): vsxmake: 支持解决方案资源管理器中的自定义源类型和 nonetype 目标
+* [#7630](https://github.com/xmake-io/xmake/pull/7630): 添加 `add_toolset` api 检查器
+* [#7637](https://github.com/xmake-io/xmake/pull/7637): 改进 mingw 下的 readline / curses 选项
+* [#7648](https://github.com/xmake-io/xmake/pull/7648): 改进错误提示
+* [#7655](https://github.com/xmake-io/xmake/pull/7655): 改进 bin2obj 的目标文件 flag 检测并添加 ppc / mips 支持
+* [#7657](https://github.com/xmake-io/xmake/pull/7657): 更新 riscv64 的 NDK sdkver
+* [#7666](https://github.com/xmake-io/xmake/pull/7666): 改进 vcpkg 包查找
+* [#7672](https://github.com/xmake-io/xmake/pull/7672): 添加下载失败回退支持
+* [#7688](https://github.com/xmake-io/xmake/pull/7688): 改进 clang 的静态 libc++ 运行时链接
+* [#7689](https://github.com/xmake-io/xmake/pull/7689): 重写 `xmake plugin --install`,将插件作为 `plugin` 类型的包从 xmake-repo 安装,复用包安装流程
+* [#7693](https://github.com/xmake-io/xmake/pull/7693): 替换 tbox 中已废弃的接口
+* 改进 elf rpath 清理和包路径处理
+* 添加 xcodebuild 检测
+* 更新内置的 tbox
+
+### Bugs 修复
+
+* [#7561](https://github.com/xmake-io/xmake/pull/7561): 修复 clang-cl 的 `-flto=thin` 回归问题
+* [#7563](https://github.com/xmake-io/xmake/pull/7563): 修复 `contains` 崩溃并改进 C++ std 模块的 PCH
+* [#7580](https://github.com/xmake-io/xmake/pull/7580): 修复 format 插件在头文件路径已是绝对路径时的处理
+* [#7581](https://github.com/xmake-io/xmake/pull/7581): 修复包加载函数中缺失的括号
+* [#7583](https://github.com/xmake-io/xmake/pull/7583): 修复 cmake 包的全局 flags 拆分
+* [#7599](https://github.com/xmake-io/xmake/pull/7599): 修复 OpenBSD 上的 gzip / tar 检测
+* [#7600](https://github.com/xmake-io/xmake/pull/7600): 修复构建缓存 memcache 的启用
+* [#7601](https://github.com/xmake-io/xmake/pull/7601): 修复 verilator 确保定义 `TRACE`
+* [#7602](https://github.com/xmake-io/xmake/pull/7602): 修复包加载 memcache
+* [#7603](https://github.com/xmake-io/xmake/pull/7603): 通过更新 tbox 修复 `rmdir`
+* [#7604](https://github.com/xmake-io/xmake/pull/7604): 修复 elf 的 rpath 清理
+* [#7608](https://github.com/xmake-io/xmake/pull/7608): 修复拼写错误并格式化命令菜单
+* [#7611](https://github.com/xmake-io/xmake/pull/7611): 修复缓存环境变量处理([#7576](https://github.com/xmake-io/xmake/issues/7576))
+* [#7622](https://github.com/xmake-io/xmake/pull/7622): 修复 deb 打包的默认 maintainer
+* [#7627](https://github.com/xmake-io/xmake/pull/7627): 修复 debuild 中的 PATH 处理
+* [#7631](https://github.com/xmake-io/xmake/pull/7631): 修复安装 deb 包时的 PATH 处理
+* [#7633](https://github.com/xmake-io/xmake/pull/7633): 修复 Meson 后端在 BSD 上的 pkg-config 路径
+* [#7640](https://github.com/xmake-io/xmake/pull/7640): 改进 emcc 检测,添加回退查找
+* [#7644](https://github.com/xmake-io/xmake/pull/7644): 修复路径转换中长路径的栈缓冲区溢出
+* [#7645](https://github.com/xmake-io/xmake/pull/7645): 修复删除只读目录
+* [#7659](https://github.com/xmake-io/xmake/pull/7659): 修复 Windows 上 emcc 的 `.exe` / `.bat` 检测
+* [#7661](https://github.com/xmake-io/xmake/pull/7661): 回退 clang 的静态 libc++ 链接
+* [#7668](https://github.com/xmake-io/xmake/pull/7668): 修复向 cmake 传递路径
+* [#7671](https://github.com/xmake-io/xmake/pull/7671): 修复 tbox 的一些 bug
+* [#7674](https://github.com/xmake-io/xmake/pull/7674): 修复 macOS 上的 Mach host send-right 泄漏
+* [#7676](https://github.com/xmake-io/xmake/pull/7676): 修复 masm 的 embed / edit 级别符号 flag
+* [#7679](https://github.com/xmake-io/xmake/pull/7679): 修复 msvc 在 C 模式下的 `set_pcheader`
+* [#7684](https://github.com/xmake-io/xmake/pull/7684): 修复 `process.open` 中错误的 Lua 栈索引
+* [#7685](https://github.com/xmake-io/xmake/pull/7685): 修复引擎资源泄漏
+* [#7687](https://github.com/xmake-io/xmake/pull/7687): 修复 ninja 生成器未给 `win.sdk.resource` 源文件批次生成构建边的问题([#7682](https://github.com/xmake-io/xmake/issues/7682))
+* [#7692](https://github.com/xmake-io/xmake/pull/7692): 修复 `build.c++.modules.tryreuse` 的依赖顺序
+* 修复 scons 的 trybuild
+
## v3.0.9
### 新特性
diff --git a/core/src/cli/xmake.lua b/core/src/cli/xmake.lua
index 7cd50cb2c..54b78806d 100644
--- a/core/src/cli/xmake.lua
+++ b/core/src/cli/xmake.lua
@@ -58,7 +58,8 @@ target("cli")
add_installfiles("$(projectdir)/../NOTICE.md")
add_installfiles("$(projectdir)/../xmake/(**.lua)")
add_installfiles("$(projectdir)/../xmake/(scripts/**)")
- add_installfiles("$(projectdir)/../xmake/(repository/templates/**)")
+ add_installfiles("$(projectdir)/../xmake/(templates/**)")
+ add_installfiles("$(projectdir)/../xmake/(repository/**)")
add_installfiles("$(projectdir)/../scripts/xrepo.bat")
add_installfiles("$(projectdir)/../scripts/xrepo.ps1")
set_prefixdir("/", {bindir = "/"})
@@ -68,7 +69,8 @@ target("cli")
else
add_installfiles("$(projectdir)/../(xmake/**.lua)", {prefixdir = "share"})
add_installfiles("$(projectdir)/../(xmake/scripts/**)", {prefixdir = "share"})
- add_installfiles("$(projectdir)/../(xmake/repository/templates/**)", {prefixdir = "share"})
+ add_installfiles("$(projectdir)/../(xmake/templates/**)", {prefixdir = "share"})
+ add_installfiles("$(projectdir)/../(xmake/repository/**)", {prefixdir = "share"})
add_installfiles("$(projectdir)/../scripts/xrepo.sh", {prefixdir = "bin", filename = "xrepo"})
end
diff --git a/core/src/cli/xmake.sh b/core/src/cli/xmake.sh
index af4e8ba36..a7ef02601 100755
--- a/core/src/cli/xmake.sh
+++ b/core/src/cli/xmake.sh
@@ -32,7 +32,8 @@ target "cli"
add_installfiles "${projectdir}/(xmake/scripts/virtualenvs/**)" "share"
add_installfiles "${projectdir}/(xmake/scripts/conan/**)" "share"
add_installfiles "${projectdir}/(xmake/scripts/module/**)" "share"
- add_installfiles "${projectdir}/(xmake/repository/templates/**)" "share"
+ add_installfiles "${projectdir}/(xmake/templates/**)" "share"
+ add_installfiles "${projectdir}/(xmake/repository/**)" "share"
add_installfiles "${projectdir}/scripts/xrepo.sh" "bin" "xrepo"
# fix os.exec() call incorrect program from /mingw64/bin. e.g. python, ..
diff --git a/core/src/tbox/tbox b/core/src/tbox/tbox
-Subproject 870b2b9e4337694bb381d55338364c8b9711b82
+Subproject 3330978dce512b5c7104683383a40ab2b14be73
diff --git a/core/src/xmake/binutils/ar/extractlib.c b/core/src/xmake/binutils/ar/extractlib.c
index d9629394a..830eb3275 100644
--- a/core/src/xmake/binutils/ar/extractlib.c
+++ b/core/src/xmake/binutils/ar/extractlib.c
@@ -212,7 +212,7 @@ tb_bool_t xm_binutils_ar_extract(tb_stream_ref_t istream, tb_char_t const *outpu
ok = tb_false;
}
- tb_stream_clos(ostream);
+ tb_stream_close(ostream);
tb_stream_exit(ostream);
tb_check_break(ok);
diff --git a/core/src/xmake/binutils/bin2c.c b/core/src/xmake/binutils/bin2c.c
index b5054c55f..3c639d059 100644
--- a/core/src/xmake/binutils/bin2c.c
+++ b/core/src/xmake/binutils/bin2c.c
@@ -209,12 +209,12 @@ tb_int_t xm_binutils_bin2c(lua_State *lua) {
} while (0);
if (istream) {
- tb_stream_clos(istream);
+ tb_stream_exit(istream);
}
istream = tb_null;
if (ostream) {
- tb_stream_clos(ostream);
+ tb_stream_exit(ostream);
}
ostream = tb_null;
diff --git a/core/src/xmake/binutils/coff/bin2coff.c b/core/src/xmake/binutils/coff/bin2coff.c
index ceb984a5c..791d4e2ba 100644
--- a/core/src/xmake/binutils/coff/bin2coff.c
+++ b/core/src/xmake/binutils/coff/bin2coff.c
@@ -303,12 +303,12 @@ tb_int_t xm_binutils_bin2coff(lua_State *lua) {
} while (0);
if (istream) {
- tb_stream_clos(istream);
+ tb_stream_exit(istream);
}
istream = tb_null;
if (ostream) {
- tb_stream_clos(ostream);
+ tb_stream_exit(ostream);
}
ostream = tb_null;
diff --git a/core/src/xmake/binutils/elf/bin2elf.c b/core/src/xmake/binutils/elf/bin2elf.c
index 76a75f8fe..585ddc681 100644
--- a/core/src/xmake/binutils/elf/bin2elf.c
+++ b/core/src/xmake/binutils/elf/bin2elf.c
@@ -34,11 +34,78 @@
* private implementation
*/
+// write an ELF struct out in the target endianness (the struct is converted in place)
+static tb_bool_t xm_binutils_bin2elf_bwrit_header_32(tb_stream_ref_t ostream, xm_elf32_header_t* h, tb_bool_t be) {
+ xm_binutils_elf32_header_conv(h, be);
+ return tb_stream_bwrit(ostream, (tb_byte_t const *)h, sizeof(*h));
+}
+static tb_bool_t xm_binutils_bin2elf_bwrit_section_32(tb_stream_ref_t ostream, xm_elf32_section_t* s, tb_bool_t be) {
+ xm_binutils_elf32_section_conv(s, be);
+ return tb_stream_bwrit(ostream, (tb_byte_t const *)s, sizeof(*s));
+}
+static tb_bool_t xm_binutils_bin2elf_bwrit_symbol_32(tb_stream_ref_t ostream, xm_elf32_symbol_t* s, tb_bool_t be) {
+ xm_binutils_elf32_symbol_conv(s, be);
+ return tb_stream_bwrit(ostream, (tb_byte_t const *)s, sizeof(*s));
+}
+static tb_bool_t xm_binutils_bin2elf_bwrit_header_64(tb_stream_ref_t ostream, xm_elf64_header_t* h, tb_bool_t be) {
+ xm_binutils_elf64_header_conv(h, be);
+ return tb_stream_bwrit(ostream, (tb_byte_t const *)h, sizeof(*h));
+}
+static tb_bool_t xm_binutils_bin2elf_bwrit_section_64(tb_stream_ref_t ostream, xm_elf64_section_t* s, tb_bool_t be) {
+ xm_binutils_elf64_section_conv(s, be);
+ return tb_stream_bwrit(ostream, (tb_byte_t const *)s, sizeof(*s));
+}
+static tb_bool_t xm_binutils_bin2elf_bwrit_symbol_64(tb_stream_ref_t ostream, xm_elf64_symbol_t* s, tb_bool_t be) {
+ xm_binutils_elf64_symbol_conv(s, be);
+ return tb_stream_bwrit(ostream, (tb_byte_t const *)s, sizeof(*s));
+}
+
+/* read the identity (class/endianness/machine/e_flags) from a reference ELF object.
+ * returns tb_true and updates the out-params on success; leaves them untouched on any failure
+ * (missing file, too small, bad magic), so the caller keeps its arch-derived defaults.
+ */
+static tb_bool_t xm_binutils_bin2elf_read_refobj(tb_char_t const *refobj,
+ tb_bool_t *pis_64bit, tb_bool_t *pis_bigendian, tb_uint16_t *pe_machine, tb_uint32_t *pe_flags) {
+ tb_assert_and_check_return_val(refobj && pis_64bit && pis_bigendian && pe_machine && pe_flags, tb_false);
+
+ tb_bool_t ok = tb_false;
+ tb_stream_ref_t stream = tb_stream_init_from_file(refobj, TB_FILE_MODE_RO);
+ do {
+ // the 32-bit ELF header is 52 bytes; the 64-bit e_flags ends at offset 52 too
+ tb_byte_t hdr[52];
+ if (!stream || !tb_stream_open(stream)) break;
+ if (!tb_stream_bread(stream, hdr, sizeof(hdr))) break;
+
+ // verify the ELF magic (0x7f 'E' 'L' 'F')
+ if (hdr[0] != 0x7f || hdr[1] != 'E' || hdr[2] != 'L' || hdr[3] != 'F') break;
+
+ tb_bool_t is_64bit = (hdr[XM_ELF_EI_CLASS] == XM_ELF_CLASS64);
+ tb_bool_t is_bigendian = (hdr[5] == XM_ELF_DATA2MSB);
+
+ // e_machine at offset 18 (2 bytes); e_flags at offset 36 (32-bit) / 48 (64-bit), in target endianness
+ tb_uint16_t e_machine = is_bigendian? tb_bits_get_u16_be(hdr + 18) : tb_bits_get_u16_le(hdr + 18);
+ tb_byte_t const *pflags = hdr + (is_64bit? 48 : 36);
+ tb_uint32_t e_flags = is_bigendian? tb_bits_get_u32_be(pflags) : tb_bits_get_u32_le(pflags);
+
+ *pis_64bit = is_64bit;
+ *pis_bigendian = is_bigendian;
+ *pe_machine = e_machine;
+ *pe_flags = e_flags;
+ ok = tb_true;
+
+ } while (0);
+ if (stream) tb_stream_exit(stream);
+ return ok;
+}
+
static tb_bool_t xm_binutils_bin2elf_dump_32(tb_stream_ref_t istream,
tb_stream_ref_t ostream,
tb_char_t const *symbol_prefix,
tb_char_t const *arch,
tb_char_t const *basename,
+ tb_bool_t bigendian,
+ tb_uint16_t e_machine,
+ tb_uint32_t e_flags,
tb_bool_t zeroend) {
tb_assert_and_check_return_val(istream && ostream, tb_false);
@@ -117,25 +184,26 @@ static tb_bool_t xm_binutils_bin2elf_dump_32(tb_stream_ref_t istream,
header.e_ident[2] = 'L';
header.e_ident[3] = 'F';
header.e_ident[XM_ELF_EI_CLASS] = XM_ELF_CLASS32;
- header.e_ident[5] = 1; // ELFDATA2LSB
+ header.e_ident[5] = bigendian? XM_ELF_DATA2MSB : XM_ELF_DATA2LSB;
header.e_ident[6] = 1; // EV_CURRENT
header.e_ident[7] = 0; // ELFOSABI_SYSV
header.e_type = 1; // ET_REL
- header.e_machine = xm_binutils_elf_get_machine(arch);
+ header.e_machine = e_machine;
header.e_version = 1;
+ header.e_flags = e_flags;
header.e_shoff = section_headers_ofs;
header.e_ehsize = header_size;
header.e_shentsize = section_header_size;
header.e_shnum = section_count;
header.e_shstrndx = 4; // .shstrtab section index
- if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&header, sizeof(header))) {
+ if (!xm_binutils_bin2elf_bwrit_header_32(ostream, &header, bigendian)) {
return tb_false;
}
// write section headers
xm_elf32_section_t section_null;
tb_memset(&section_null, 0, sizeof(section_null));
- if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&section_null, sizeof(section_null))) {
+ if (!xm_binutils_bin2elf_bwrit_section_32(ostream, &section_null, bigendian)) {
return tb_false;
}
@@ -148,7 +216,7 @@ static tb_bool_t xm_binutils_bin2elf_dump_32(tb_stream_ref_t istream,
section_rodata.sh_offset = rodata_ofs;
section_rodata.sh_size = rodata_size;
section_rodata.sh_addralign = 4;
- if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&section_rodata, sizeof(section_rodata))) {
+ if (!xm_binutils_bin2elf_bwrit_section_32(ostream, &section_rodata, bigendian)) {
return tb_false;
}
@@ -163,7 +231,7 @@ static tb_bool_t xm_binutils_bin2elf_dump_32(tb_stream_ref_t istream,
section_symtab.sh_info = 1; // first global symbol index
section_symtab.sh_addralign = 4;
section_symtab.sh_entsize = sizeof(xm_elf32_symbol_t);
- if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&section_symtab, sizeof(section_symtab))) {
+ if (!xm_binutils_bin2elf_bwrit_section_32(ostream, &section_symtab, bigendian)) {
return tb_false;
}
@@ -175,7 +243,7 @@ static tb_bool_t xm_binutils_bin2elf_dump_32(tb_stream_ref_t istream,
section_strtab.sh_offset = strtab_ofs;
section_strtab.sh_size = strtab_size;
section_strtab.sh_addralign = 1;
- if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&section_strtab, sizeof(section_strtab))) {
+ if (!xm_binutils_bin2elf_bwrit_section_32(ostream, &section_strtab, bigendian)) {
return tb_false;
}
@@ -187,7 +255,7 @@ static tb_bool_t xm_binutils_bin2elf_dump_32(tb_stream_ref_t istream,
section_shstrtab.sh_offset = shstrtab_ofs;
section_shstrtab.sh_size = shstrtab_size;
section_shstrtab.sh_addralign = 1;
- if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&section_shstrtab, sizeof(section_shstrtab))) {
+ if (!xm_binutils_bin2elf_bwrit_section_32(ostream, &section_shstrtab, bigendian)) {
return tb_false;
}
@@ -200,7 +268,7 @@ static tb_bool_t xm_binutils_bin2elf_dump_32(tb_stream_ref_t istream,
section_note_gnu_stack.sh_offset = shstrtab_ofs + shstrtab_size; // after .shstrtab
section_note_gnu_stack.sh_size = 0; // empty section
section_note_gnu_stack.sh_addralign = 1;
- if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&section_note_gnu_stack, sizeof(section_note_gnu_stack))) {
+ if (!xm_binutils_bin2elf_bwrit_section_32(ostream, &section_note_gnu_stack, bigendian)) {
return tb_false;
}
@@ -230,7 +298,7 @@ static tb_bool_t xm_binutils_bin2elf_dump_32(tb_stream_ref_t istream,
// symbol 0: NULL symbol
xm_elf32_symbol_t sym_null;
tb_memset(&sym_null, 0, sizeof(sym_null));
- if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&sym_null, sizeof(sym_null))) {
+ if (!xm_binutils_bin2elf_bwrit_symbol_32(ostream, &sym_null, bigendian)) {
return tb_false;
}
@@ -242,7 +310,7 @@ static tb_bool_t xm_binutils_bin2elf_dump_32(tb_stream_ref_t istream,
sym_start.st_shndx = 1; // .rodata section index
sym_start.st_value = 0;
sym_start.st_size = 0;
- if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&sym_start, sizeof(sym_start))) {
+ if (!xm_binutils_bin2elf_bwrit_symbol_32(ostream, &sym_start, bigendian)) {
return tb_false;
}
@@ -254,7 +322,7 @@ static tb_bool_t xm_binutils_bin2elf_dump_32(tb_stream_ref_t istream,
sym_end.st_shndx = 1; // .rodata section index
sym_end.st_value = rodata_size;
sym_end.st_size = 0;
- if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&sym_end, sizeof(sym_end))) {
+ if (!xm_binutils_bin2elf_bwrit_symbol_32(ostream, &sym_end, bigendian)) {
return tb_false;
}
@@ -339,6 +407,9 @@ static tb_bool_t xm_binutils_bin2elf_dump_64(tb_stream_ref_t istream,
tb_char_t const *symbol_prefix,
tb_char_t const *arch,
tb_char_t const *basename,
+ tb_bool_t bigendian,
+ tb_uint16_t e_machine,
+ tb_uint32_t e_flags,
tb_bool_t zeroend) {
tb_assert_and_check_return_val(istream && ostream, tb_false);
@@ -417,25 +488,26 @@ static tb_bool_t xm_binutils_bin2elf_dump_64(tb_stream_ref_t istream,
header.e_ident[2] = 'L';
header.e_ident[3] = 'F';
header.e_ident[XM_ELF_EI_CLASS] = XM_ELF_CLASS64;
- header.e_ident[5] = 1; // ELFDATA2LSB
+ header.e_ident[5] = bigendian? XM_ELF_DATA2MSB : XM_ELF_DATA2LSB;
header.e_ident[6] = 1; // EV_CURRENT
header.e_ident[7] = 0; // ELFOSABI_SYSV
header.e_type = 1; // ET_REL
- header.e_machine = xm_binutils_elf_get_machine(arch);
+ header.e_machine = e_machine;
header.e_version = 1;
+ header.e_flags = e_flags;
header.e_shoff = section_headers_ofs;
header.e_ehsize = header_size;
header.e_shentsize = section_header_size;
header.e_shnum = section_count;
header.e_shstrndx = 4; // .shstrtab section index
- if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&header, sizeof(header))) {
+ if (!xm_binutils_bin2elf_bwrit_header_64(ostream, &header, bigendian)) {
return tb_false;
}
// write section headers
xm_elf64_section_t section_null;
tb_memset(&section_null, 0, sizeof(section_null));
- if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&section_null, sizeof(section_null))) {
+ if (!xm_binutils_bin2elf_bwrit_section_64(ostream, &section_null, bigendian)) {
return tb_false;
}
@@ -448,7 +520,7 @@ static tb_bool_t xm_binutils_bin2elf_dump_64(tb_stream_ref_t istream,
section_rodata.sh_offset = rodata_ofs;
section_rodata.sh_size = rodata_size;
section_rodata.sh_addralign = 8;
- if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&section_rodata, sizeof(section_rodata))) {
+ if (!xm_binutils_bin2elf_bwrit_section_64(ostream, &section_rodata, bigendian)) {
return tb_false;
}
@@ -463,7 +535,7 @@ static tb_bool_t xm_binutils_bin2elf_dump_64(tb_stream_ref_t istream,
section_symtab.sh_info = 1; // first global symbol index
section_symtab.sh_addralign = 8;
section_symtab.sh_entsize = sizeof(xm_elf64_symbol_t);
- if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&section_symtab, sizeof(section_symtab))) {
+ if (!xm_binutils_bin2elf_bwrit_section_64(ostream, &section_symtab, bigendian)) {
return tb_false;
}
@@ -475,7 +547,7 @@ static tb_bool_t xm_binutils_bin2elf_dump_64(tb_stream_ref_t istream,
section_strtab.sh_offset = strtab_ofs;
section_strtab.sh_size = strtab_size;
section_strtab.sh_addralign = 1;
- if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&section_strtab, sizeof(section_strtab))) {
+ if (!xm_binutils_bin2elf_bwrit_section_64(ostream, &section_strtab, bigendian)) {
return tb_false;
}
@@ -487,7 +559,7 @@ static tb_bool_t xm_binutils_bin2elf_dump_64(tb_stream_ref_t istream,
section_shstrtab.sh_offset = shstrtab_ofs; // points to initial null byte
section_shstrtab.sh_size = shstrtab_size; // size includes initial null and all strings
section_shstrtab.sh_addralign = 1;
- if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&section_shstrtab, sizeof(section_shstrtab))) {
+ if (!xm_binutils_bin2elf_bwrit_section_64(ostream, &section_shstrtab, bigendian)) {
return tb_false;
}
@@ -500,7 +572,7 @@ static tb_bool_t xm_binutils_bin2elf_dump_64(tb_stream_ref_t istream,
section_note_gnu_stack.sh_offset = shstrtab_ofs + shstrtab_size; // after .shstrtab
section_note_gnu_stack.sh_size = 0; // empty section
section_note_gnu_stack.sh_addralign = 1;
- if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&section_note_gnu_stack, sizeof(section_note_gnu_stack))) {
+ if (!xm_binutils_bin2elf_bwrit_section_64(ostream, &section_note_gnu_stack, bigendian)) {
return tb_false;
}
@@ -530,7 +602,7 @@ static tb_bool_t xm_binutils_bin2elf_dump_64(tb_stream_ref_t istream,
// symbol 0: NULL symbol
xm_elf64_symbol_t sym_null;
tb_memset(&sym_null, 0, sizeof(sym_null));
- if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&sym_null, sizeof(sym_null))) {
+ if (!xm_binutils_bin2elf_bwrit_symbol_64(ostream, &sym_null, bigendian)) {
return tb_false;
}
@@ -542,7 +614,7 @@ static tb_bool_t xm_binutils_bin2elf_dump_64(tb_stream_ref_t istream,
sym_start.st_shndx = 1; // .rodata section index
sym_start.st_value = 0;
sym_start.st_size = 0;
- if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&sym_start, sizeof(sym_start))) {
+ if (!xm_binutils_bin2elf_bwrit_symbol_64(ostream, &sym_start, bigendian)) {
return tb_false;
}
@@ -554,7 +626,7 @@ static tb_bool_t xm_binutils_bin2elf_dump_64(tb_stream_ref_t istream,
sym_end.st_shndx = 1; // .rodata section index
sym_end.st_value = rodata_size;
sym_end.st_size = 0;
- if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&sym_end, sizeof(sym_end))) {
+ if (!xm_binutils_bin2elf_bwrit_symbol_64(ostream, &sym_end, bigendian)) {
return tb_false;
}
@@ -640,7 +712,7 @@ static tb_bool_t xm_binutils_bin2elf_dump_64(tb_stream_ref_t istream,
/* generate ELF object file from binary file
*
- * local ok, errors = binutils.bin2elf(binaryfile, outputfile, symbol_prefix, arch, basename, zeroend)
+ * local ok, errors = binutils.bin2elf(binaryfile, outputfile, symbol_prefix, arch, basename, zeroend, refobj)
*/
tb_int_t xm_binutils_bin2elf(lua_State *lua) {
tb_assert_and_check_return_val(lua, 0);
@@ -665,6 +737,13 @@ tb_int_t xm_binutils_bin2elf(lua_State *lua) {
// get zeroend (optional, default: false)
tb_bool_t zeroend = lua_toboolean(lua, 6);
+ /* get the reference object (optional): a real object emitted by the target toolchain.
+ * we mirror its class/endianness/machine/e_flags so the output matches exactly, instead of
+ * guessing from the (sometimes ambiguous) arch name. when absent/unreadable we fall back to
+ * deriving everything from the arch name.
+ */
+ tb_char_t const *refobj = lua_isstring(lua, 7) ? lua_tostring(lua, 7) : tb_null;
+
// do dump
tb_bool_t ok = tb_false;
tb_stream_ref_t istream = tb_stream_init_from_file(binaryfile, TB_FILE_MODE_RO);
@@ -683,16 +762,23 @@ tb_int_t xm_binutils_bin2elf(lua_State *lua) {
break;
}
- // choose 32-bit or 64-bit ELF based on architecture
+ /* resolve class/endian/machine/flags: derive from the arch name, then mirror the
+ * reference object if one was given and is a readable ELF (it wins over the heuristic) */
tb_bool_t is_64bit = xm_binutils_elf_is_64bit(arch);
+ tb_bool_t is_bigendian = xm_binutils_elf_is_bigendian(arch);
+ tb_uint16_t e_machine = xm_binutils_elf_get_machine(arch);
+ tb_uint32_t e_flags = xm_binutils_elf_get_flags(arch);
+ if (refobj) {
+ xm_binutils_bin2elf_read_refobj(refobj, &is_64bit, &is_bigendian, &e_machine, &e_flags);
+ }
if (is_64bit) {
- if (!xm_binutils_bin2elf_dump_64(istream, ostream, symbol_prefix, arch, basename, zeroend)) {
+ if (!xm_binutils_bin2elf_dump_64(istream, ostream, symbol_prefix, arch, basename, is_bigendian, e_machine, e_flags, zeroend)) {
lua_pushboolean(lua, tb_false);
lua_pushfstring(lua, "bin2elf: dump data failed");
break;
}
} else {
- if (!xm_binutils_bin2elf_dump_32(istream, ostream, symbol_prefix, arch, basename, zeroend)) {
+ if (!xm_binutils_bin2elf_dump_32(istream, ostream, symbol_prefix, arch, basename, is_bigendian, e_machine, e_flags, zeroend)) {
lua_pushboolean(lua, tb_false);
lua_pushfstring(lua, "bin2elf: dump data failed");
break;
@@ -705,11 +791,11 @@ tb_int_t xm_binutils_bin2elf(lua_State *lua) {
} while (0);
if (istream)
- tb_stream_clos(istream);
+ tb_stream_exit(istream);
istream = tb_null;
if (ostream)
- tb_stream_clos(ostream);
+ tb_stream_exit(ostream);
ostream = tb_null;
return ok ? 1 : 2;
diff --git a/core/src/xmake/binutils/elf/prefix.h b/core/src/xmake/binutils/elf/prefix.h
index 2072ee879..9f18864df 100644
--- a/core/src/xmake/binutils/elf/prefix.h
+++ b/core/src/xmake/binutils/elf/prefix.h
@@ -56,6 +56,27 @@
#define XM_ELF_MACHINE_WASM 0xe7
#define XM_ELF_MACHINE_LOONGARCH 0x102
+// ELF data encoding (e_ident[EI_DATA])
+#define XM_ELF_DATA2LSB 1
+#define XM_ELF_DATA2MSB 2
+
+// RISC-V e_flags (arch/riscv/include/uapi/asm/elf.h)
+#define XM_EF_RISCV_RVC 0x0001
+#define XM_EF_RISCV_FLOAT_ABI_SINGLE 0x0002
+#define XM_EF_RISCV_FLOAT_ABI_DOUBLE 0x0004
+
+// LoongArch e_flags (LoongArch ELF psABI)
+#define XM_EF_LOONGARCH_ABI_DOUBLE_FLOAT 0x3
+#define XM_EF_LOONGARCH_OBJABI_V1 0x40
+
+// PowerPC64 e_flags: the ELF ABI version is stored in the low 2 bits (see bfd/elf64-ppc.c)
+#define XM_EF_PPC64_ABI_V1 0x1
+#define XM_EF_PPC64_ABI_V2 0x2
+
+// MIPS e_flags (binutils include/elf/mips.h)
+#define XM_EF_MIPS_CPIC 0x00000004 // call-PIC: linkable with both PIC and non-PIC objects
+#define XM_EF_MIPS_ABI_O32 0x00001000 // the original 32-bit "o32" ABI
+
#define XM_ELF_SHT_PROGBITS 0x1
#define XM_ELF_SHT_SYMTAB 0x2
#define XM_ELF_SHT_STRTAB 0x3
@@ -297,6 +318,149 @@ static __tb_inline__ tb_bool_t xm_binutils_elf_is_64bit(tb_char_t const *arch) {
return xm_binutils_arch_is_64bit(arch);
}
+/* check if architecture is big-endian
+ *
+ * @param arch the architecture string
+ * @return tb_true if big-endian, tb_false otherwise
+ */
+static __tb_inline__ tb_bool_t xm_binutils_elf_is_bigendian(tb_char_t const *arch) {
+ return xm_binutils_arch_is_bigendian(arch);
+}
+
+/* get the default e_flags for the given architecture
+ *
+ * Some architectures (RISC-V, LoongArch) encode the ABI (e.g. float ABI) in e_flags.
+ * The linker refuses to merge objects whose ABI flags are incompatible, so a data-only
+ * object generated with e_flags == 0 (soft-float) would fail to link against a normal
+ * double-float toolchain. We default to the flags used by the common GNU toolchains.
+ *
+ * @param arch the architecture string
+ * @return the e_flags value
+ */
+static __tb_inline__ tb_uint32_t xm_binutils_elf_get_flags(tb_char_t const *arch) {
+ if (!arch) {
+ return 0;
+ }
+ // RISC-V: default to RVC + double-float ABI to match the common rv32/rv64 "gc" toolchains
+ if (tb_strncmp(arch, "riscv", 5) == 0) {
+ return XM_EF_RISCV_RVC | XM_EF_RISCV_FLOAT_ABI_DOUBLE;
+ }
+ // LoongArch: default to double-float ABI (lp64d/ilp32d) + object ABI v1
+ else if (tb_strncmp(arch, "loongarch", 9) == 0 || tb_strncmp(arch, "loong64", 7) == 0) {
+ return XM_EF_LOONGARCH_ABI_DOUBLE_FLOAT | XM_EF_LOONGARCH_OBJABI_V1;
+ }
+ /* PowerPC64: encode the ELF ABI version in e_flags.
+ * little-endian ppc64le uses the OpenPOWER ELFv2 ABI, big-endian ppc64 uses ELFv1,
+ * matching the gcc/clang defaults (-mabi=elfv2 on LE, -mabi=elfv1 on BE).
+ * 32-bit PowerPC does not carry an ABI version (e_flags == 0).
+ */
+ else if (tb_strncmp(arch, "ppc64", 5) == 0 || tb_strncmp(arch, "powerpc64", 9) == 0) {
+ return xm_binutils_arch_is_bigendian(arch)? XM_EF_PPC64_ABI_V1 : XM_EF_PPC64_ABI_V2;
+ }
+ /* MIPS: mark the object as CPIC so it links against both PIC and non-PIC objects, and
+ * tag the 32-bit variants with the o32 ABI to match the common GNU toolchains (n64 is
+ * implied by ELFCLASS64, so mips64/mips64el carry no ABI bit). the ISA level (top nibble)
+ * and fp/NaN bits are left at 0 (== "any") on purpose: overclaiming them would make the
+ * linker reject otherwise-compatible objects.
+ */
+ else if (tb_strncmp(arch, "mips", 4) == 0) {
+ tb_uint32_t flags = XM_EF_MIPS_CPIC;
+ if (!xm_binutils_arch_is_64bit(arch)) {
+ flags |= XM_EF_MIPS_ABI_O32;
+ }
+ return flags;
+ }
+ return 0;
+}
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * endianness-aware serialization
+ *
+ * The dump code fills the ELF structs in the host's native byte order. Before writing them
+ * out, each multi-byte field must be converted to the *target* endianness (which may differ
+ * from the host, e.g. generating a big-endian s390x object on a little-endian host).
+ */
+
+static __tb_inline__ tb_uint16_t xm_binutils_elf_conv_u16(tb_uint16_t x, tb_bool_t bigendian) {
+ return bigendian? tb_bits_ne_to_be_u16(x) : tb_bits_ne_to_le_u16(x);
+}
+static __tb_inline__ tb_uint32_t xm_binutils_elf_conv_u32(tb_uint32_t x, tb_bool_t bigendian) {
+ return bigendian? tb_bits_ne_to_be_u32(x) : tb_bits_ne_to_le_u32(x);
+}
+static __tb_inline__ tb_uint64_t xm_binutils_elf_conv_u64(tb_uint64_t x, tb_bool_t bigendian) {
+ return bigendian? tb_bits_ne_to_be_u64(x) : tb_bits_ne_to_le_u64(x);
+}
+
+// convert a 32-bit ELF header to the target endianness in place (e_ident is byte data, untouched)
+static __tb_inline__ void xm_binutils_elf32_header_conv(xm_elf32_header_t* h, tb_bool_t be) {
+ h->e_type = xm_binutils_elf_conv_u16(h->e_type, be);
+ h->e_machine = xm_binutils_elf_conv_u16(h->e_machine, be);
+ h->e_version = xm_binutils_elf_conv_u32(h->e_version, be);
+ h->e_entry = xm_binutils_elf_conv_u32(h->e_entry, be);
+ h->e_phoff = xm_binutils_elf_conv_u32(h->e_phoff, be);
+ h->e_shoff = xm_binutils_elf_conv_u32(h->e_shoff, be);
+ h->e_flags = xm_binutils_elf_conv_u32(h->e_flags, be);
+ h->e_ehsize = xm_binutils_elf_conv_u16(h->e_ehsize, be);
+ h->e_phentsize = xm_binutils_elf_conv_u16(h->e_phentsize, be);
+ h->e_phnum = xm_binutils_elf_conv_u16(h->e_phnum, be);
+ h->e_shentsize = xm_binutils_elf_conv_u16(h->e_shentsize, be);
+ h->e_shnum = xm_binutils_elf_conv_u16(h->e_shnum, be);
+ h->e_shstrndx = xm_binutils_elf_conv_u16(h->e_shstrndx, be);
+}
+static __tb_inline__ void xm_binutils_elf32_section_conv(xm_elf32_section_t* s, tb_bool_t be) {
+ s->sh_name = xm_binutils_elf_conv_u32(s->sh_name, be);
+ s->sh_type = xm_binutils_elf_conv_u32(s->sh_type, be);
+ s->sh_flags = xm_binutils_elf_conv_u32(s->sh_flags, be);
+ s->sh_addr = xm_binutils_elf_conv_u32(s->sh_addr, be);
+ s->sh_offset = xm_binutils_elf_conv_u32(s->sh_offset, be);
+ s->sh_size = xm_binutils_elf_conv_u32(s->sh_size, be);
+ s->sh_link = xm_binutils_elf_conv_u32(s->sh_link, be);
+ s->sh_info = xm_binutils_elf_conv_u32(s->sh_info, be);
+ s->sh_addralign = xm_binutils_elf_conv_u32(s->sh_addralign, be);
+ s->sh_entsize = xm_binutils_elf_conv_u32(s->sh_entsize, be);
+}
+static __tb_inline__ void xm_binutils_elf32_symbol_conv(xm_elf32_symbol_t* s, tb_bool_t be) {
+ s->st_name = xm_binutils_elf_conv_u32(s->st_name, be);
+ s->st_value = xm_binutils_elf_conv_u32(s->st_value, be);
+ s->st_size = xm_binutils_elf_conv_u32(s->st_size, be);
+ s->st_shndx = xm_binutils_elf_conv_u16(s->st_shndx, be);
+ // st_info and st_other are single bytes, untouched
+}
+static __tb_inline__ void xm_binutils_elf64_header_conv(xm_elf64_header_t* h, tb_bool_t be) {
+ h->e_type = xm_binutils_elf_conv_u16(h->e_type, be);
+ h->e_machine = xm_binutils_elf_conv_u16(h->e_machine, be);
+ h->e_version = xm_binutils_elf_conv_u32(h->e_version, be);
+ h->e_entry = xm_binutils_elf_conv_u64(h->e_entry, be);
+ h->e_phoff = xm_binutils_elf_conv_u64(h->e_phoff, be);
+ h->e_shoff = xm_binutils_elf_conv_u64(h->e_shoff, be);
+ h->e_flags = xm_binutils_elf_conv_u32(h->e_flags, be);
+ h->e_ehsize = xm_binutils_elf_conv_u16(h->e_ehsize, be);
+ h->e_phentsize = xm_binutils_elf_conv_u16(h->e_phentsize, be);
+ h->e_phnum = xm_binutils_elf_conv_u16(h->e_phnum, be);
+ h->e_shentsize = xm_binutils_elf_conv_u16(h->e_shentsize, be);
+ h->e_shnum = xm_binutils_elf_conv_u16(h->e_shnum, be);
+ h->e_shstrndx = xm_binutils_elf_conv_u16(h->e_shstrndx, be);
+}
+static __tb_inline__ void xm_binutils_elf64_section_conv(xm_elf64_section_t* s, tb_bool_t be) {
+ s->sh_name = xm_binutils_elf_conv_u32(s->sh_name, be);
+ s->sh_type = xm_binutils_elf_conv_u32(s->sh_type, be);
+ s->sh_flags = xm_binutils_elf_conv_u64(s->sh_flags, be);
+ s->sh_addr = xm_binutils_elf_conv_u64(s->sh_addr, be);
+ s->sh_offset = xm_binutils_elf_conv_u64(s->sh_offset, be);
+ s->sh_size = xm_binutils_elf_conv_u64(s->sh_size, be);
+ s->sh_link = xm_binutils_elf_conv_u32(s->sh_link, be);
+ s->sh_info = xm_binutils_elf_conv_u32(s->sh_info, be);
+ s->sh_addralign = xm_binutils_elf_conv_u64(s->sh_addralign, be);
+ s->sh_entsize = xm_binutils_elf_conv_u64(s->sh_entsize, be);
+}
+static __tb_inline__ void xm_binutils_elf64_symbol_conv(xm_elf64_symbol_t* s, tb_bool_t be) {
+ s->st_name = xm_binutils_elf_conv_u32(s->st_name, be);
+ s->st_shndx = xm_binutils_elf_conv_u16(s->st_shndx, be);
+ s->st_value = xm_binutils_elf_conv_u64(s->st_value, be);
+ s->st_size = xm_binutils_elf_conv_u64(s->st_size, be);
+ // st_info and st_other are single bytes, untouched
+}
+
/* //////////////////////////////////////////////////////////////////////////////////////
* readsyms inline implementation
*/
diff --git a/core/src/xmake/binutils/extractlib.c b/core/src/xmake/binutils/extractlib.c
index fbfbacfef..735859ade 100644
--- a/core/src/xmake/binutils/extractlib.c
+++ b/core/src/xmake/binutils/extractlib.c
@@ -129,7 +129,7 @@ tb_int_t xm_binutils_extractlib(lua_State *lua) {
} while (0);
if (istream) {
- tb_stream_clos(istream);
+ tb_stream_close(istream);
tb_stream_exit(istream);
}
diff --git a/core/src/xmake/binutils/macho/bin2macho.c b/core/src/xmake/binutils/macho/bin2macho.c
index 0b9dad0c2..c3c1c7a5c 100644
--- a/core/src/xmake/binutils/macho/bin2macho.c
+++ b/core/src/xmake/binutils/macho/bin2macho.c
@@ -606,12 +606,12 @@ tb_int_t xm_binutils_bin2macho(lua_State *lua) {
} while (0);
if (istream) {
- tb_stream_clos(istream);
+ tb_stream_exit(istream);
}
istream = tb_null;
if (ostream) {
- tb_stream_clos(ostream);
+ tb_stream_exit(ostream);
}
ostream = tb_null;
diff --git a/core/src/xmake/binutils/prefix.h b/core/src/xmake/binutils/prefix.h
index 608424958..ba7b06f12 100644
--- a/core/src/xmake/binutils/prefix.h
+++ b/core/src/xmake/binutils/prefix.h
@@ -205,8 +205,8 @@ static __tb_inline__ tb_bool_t xm_binutils_arch_is_64bit(tb_char_t const *arch)
else if (tb_strcmp(arch, "s390x") == 0) {
return tb_true;
}
- // LoongArch64
- else if (tb_strncmp(arch, "loongarch64", 11) == 0) {
+ // LoongArch64 (xmake uses "loong64" as the canonical arch name)
+ else if (tb_strncmp(arch, "loongarch64", 11) == 0 || tb_strcmp(arch, "loong64") == 0) {
return tb_true;
}
// WebAssembly 64
@@ -220,5 +220,44 @@ static __tb_inline__ tb_bool_t xm_binutils_arch_is_64bit(tb_char_t const *arch)
return tb_false;
}
+/* check if architecture is big-endian
+ *
+ * @param arch the architecture string
+ * @return tb_true if big-endian, tb_false otherwise
+ */
+static __tb_inline__ tb_bool_t xm_binutils_arch_is_bigendian(tb_char_t const *arch) {
+ if (!arch) {
+ return tb_false;
+ }
+ // s390/s390x are always big-endian
+ if (tb_strcmp(arch, "s390x") == 0 || tb_strcmp(arch, "s390") == 0) {
+ return tb_true;
+ }
+ // SPARC is big-endian
+ else if (tb_strncmp(arch, "sparc", 5) == 0) {
+ return tb_true;
+ }
+ // MIPS big-endian variants (mips, mips64), the little-endian ones end with "el" (mipsel, mips64el)
+ else if (tb_strncmp(arch, "mips", 4) == 0) {
+ return tb_strstr(arch, "el") == tb_null;
+ }
+ /* PowerPC endianness:
+ * - an explicit "le"/"be" suffix always wins (ppc64le/powerpc64le, ppc64be)
+ * - otherwise 64-bit ppc64/powerpc64 defaults to little-endian: xmake has no separate
+ * ppc64le arch (find_platform maps powerpc64le -> ppc64) and ppc64le is the dominant
+ * modern target, so a plain "ppc64" is treated as ppc64le (LE + OpenPOWER ELFv2)
+ * - 32-bit ppc stays traditional big-endian
+ */
+ else if (tb_strncmp(arch, "ppc", 3) == 0 || tb_strncmp(arch, "powerpc", 7) == 0) {
+ if (tb_strstr(arch, "le")) {
+ return tb_false;
+ } else if (tb_strstr(arch, "be")) {
+ return tb_true;
+ }
+ return !xm_binutils_arch_is_64bit(arch);
+ }
+ return tb_false;
+}
+
#endif
diff --git a/core/src/xmake/binutils/readsyms.c b/core/src/xmake/binutils/readsyms.c
index 2c119a538..cac501a69 100644
--- a/core/src/xmake/binutils/readsyms.c
+++ b/core/src/xmake/binutils/readsyms.c
@@ -82,7 +82,7 @@ tb_int_t xm_binutils_readsyms(lua_State *lua) {
lua_pushfstring(lua, "cannot detect file format");
break;
}
-
+
// create result list
lua_newtable(lua);
@@ -160,7 +160,7 @@ tb_int_t xm_binutils_readsyms(lua_State *lua) {
} while (0);
if (istream) {
- tb_stream_clos(istream);
+ tb_stream_exit(istream);
}
istream = tb_null;
diff --git a/core/src/xmake/engine.c b/core/src/xmake/engine.c
index b93b29bb0..b37817cc9 100644
--- a/core/src/xmake/engine.c
+++ b/core/src/xmake/engine.c
@@ -814,6 +814,10 @@ static tb_bool_t xm_engine_save_arguments(xm_engine_t *engine, tb_int_t argc, tb
lua_rawseti(engine->lua, -2, (int)lua_objlen(engine->lua, -2) + 1);
}
+#if defined(TB_CONFIG_OS_WINDOWS) && !defined(TB_COMPILER_LIKE_UNIX)
+ LocalFree(argvw);
+#endif
+
// _ARGV = table_new
lua_setglobal(engine->lua, "_ARGV");
return tb_true;
@@ -1445,8 +1449,8 @@ static tb_bool_t xm_engine_extract_programfiles_impl(xm_engine_t *engine,
if (tb_stream_open(stream)) {
tb_stream_bwrit(stream, p, n);
- tb_stream_exit(stream);
}
+ tb_stream_exit(stream);
p += n;
}
diff --git a/core/src/xmake/engine_pool.c b/core/src/xmake/engine_pool.c
index 39bdb1339..b9df842eb 100644
--- a/core/src/xmake/engine_pool.c
+++ b/core/src/xmake/engine_pool.c
@@ -43,6 +43,18 @@
#define XM_ENGINE_POOL (TB_SINGLETON_TYPE_USER + 4)
/* //////////////////////////////////////////////////////////////////////////////////////
+ * globals
+ */
+
+/* the engine pool lock
+ *
+ * the pool is a singleton shared by all worker threads, which may alloc/free engines
+ * concurrently (e.g. parallel batchcmds:lua/vlua jobs run in native threads), so we must
+ * protect the underlying list against data races, otherwise it will be corrupted and crash.
+ */
+static tb_spinlock_t g_engine_pool_lock = TB_SPINLOCK_INIT;
+
+/* //////////////////////////////////////////////////////////////////////////////////////
* private implementation
*/
static tb_handle_t xm_engine_pool_instance_init(tb_cpointer_t *ppriv) {
@@ -83,17 +95,22 @@ tb_void_t xm_engine_pool_exit(xm_engine_pool_ref_t engine_pool) {
xm_engine_ref_t xm_engine_pool_alloc(xm_engine_pool_ref_t engine_pool) {
xm_engine_ref_t engine = tb_null;
+ tb_spinlock_enter(&g_engine_pool_lock);
if (tb_single_list_size(engine_pool) > 0) {
engine = (xm_engine_ref_t)tb_single_list_head(engine_pool);
tb_single_list_remove_head(engine_pool);
}
+ tb_spinlock_leave(&g_engine_pool_lock);
return engine;
}
tb_bool_t xm_engine_pool_free(xm_engine_pool_ref_t engine_pool, xm_engine_ref_t engine) {
+ tb_bool_t ok = tb_false;
+ tb_spinlock_enter(&g_engine_pool_lock);
if (tb_single_list_size(engine_pool) < XM_ENGINE_POOL_MAXN) {
tb_single_list_insert_tail(engine_pool, engine);
- return tb_true;
+ ok = tb_true;
}
- return tb_false;
+ tb_spinlock_leave(&g_engine_pool_lock);
+ return ok;
}
diff --git a/core/src/xmake/io/file_close.c b/core/src/xmake/io/file_close.c
index bb5cf1060..303f9f002 100644
--- a/core/src/xmake/io/file_close.c
+++ b/core/src/xmake/io/file_close.c
@@ -68,7 +68,7 @@ tb_int_t xm_io_file_close(lua_State *lua) {
}
// close file
- tb_stream_clos(file->u.file_ref);
+ tb_stream_close(file->u.file_ref);
file->u.file_ref = tb_null;
// exit fstream
diff --git a/core/src/xmake/io/file_write.c b/core/src/xmake/io/file_write.c
index fdfd5afe7..5f860d641 100644
--- a/core/src/xmake/io/file_write.c
+++ b/core/src/xmake/io/file_write.c
@@ -110,7 +110,7 @@ static tb_void_t xm_io_file_write_std(xm_io_file_t *file, tb_byte_t const *data,
tb_check_return(type != XM_IO_FILE_TYPE_STDIN);
// write data to stdout/stderr
- tb_stdfile_writ(file->u.std_ref, data, size);
+ tb_stdfile_write(file->u.std_ref, data, size);
}
/* //////////////////////////////////////////////////////////////////////////////////////
diff --git a/core/src/xmake/os/cpuinfo.c b/core/src/xmake/os/cpuinfo.c
index 47542a4b4..e931f7912 100644
--- a/core/src/xmake/os/cpuinfo.c
+++ b/core/src/xmake/os/cpuinfo.c
@@ -70,8 +70,9 @@ static tb_float_t xm_os_cpuinfo_usagerate() {
processor_info_array_t cpuinfo;
mach_msg_type_number_t cpuinfo_count;
static tb_hong_t s_time = 0;
+ host_t host = mach_host_self();
if (tb_mclock() - s_time > 1000 &&
- host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &cpu_count, &cpuinfo, &cpuinfo_count) ==
+ host_processor_info(host, PROCESSOR_CPU_LOAD_INFO, &cpu_count, &cpuinfo, &cpuinfo_count) ==
KERN_SUCCESS) {
static processor_info_array_t s_cpuinfo_prev = tb_null;
static mach_msg_type_number_t s_cpuinfo_count_prev = 0;
@@ -100,6 +101,7 @@ static tb_float_t xm_os_cpuinfo_usagerate() {
s_cpuinfo_prev = cpuinfo;
s_cpuinfo_count_prev = cpuinfo_count;
}
+ mach_port_deallocate(mach_task_self(), host);
return cpu_count > 0 ? usagerate / cpu_count : 0;
#elif defined(TB_CONFIG_OS_WINDOWS)
// kernel include idle_time
diff --git a/core/src/xmake/os/meminfo.c b/core/src/xmake/os/meminfo.c
index 96db76f0d..0671d5311 100644
--- a/core/src/xmake/os/meminfo.c
+++ b/core/src/xmake/os/meminfo.c
@@ -61,7 +61,10 @@ static tb_bool_t xm_os_meminfo_stats(tb_int_t *ptotalsize, tb_int_t *pavailsize)
#if defined(TB_CONFIG_OS_MACOSX)
vm_statistics64_data_t vmstat;
mach_msg_type_number_t count = HOST_VM_INFO64_COUNT;
- if (host_statistics64(mach_host_self(), HOST_VM_INFO64, (host_info_t)&vmstat, &count) == KERN_SUCCESS) {
+ host_t host = mach_host_self();
+ kern_return_t result = host_statistics64(host, HOST_VM_INFO64, (host_info_t)&vmstat, &count);
+ mach_port_deallocate(mach_task_self(), host);
+ if (result == KERN_SUCCESS) {
tb_int_t pagesize = (tb_int_t)tb_page_size();
tb_int64_t totalsize = (tb_int64_t)(vmstat.inactive_count + vmstat.free_count + vmstat.active_count +
vmstat.wire_count
diff --git a/core/src/xmake/process/open.c b/core/src/xmake/process/open.c
index b6cf272b2..5ae958f8d 100644
--- a/core/src/xmake/process/open.c
+++ b/core/src/xmake/process/open.c
@@ -75,7 +75,7 @@ tb_int_t xm_process_open(lua_State *lua) {
// get curdir
lua_pushstring(lua, "curdir");
- lua_gettable(lua, 3);
+ lua_gettable(lua, 2);
attr.curdir = lua_tostring(lua, -1);
lua_pop(lua, 1);
@@ -100,7 +100,7 @@ tb_int_t xm_process_open(lua_State *lua) {
// get infile
if (!inpath) {
lua_pushstring(lua, "infile");
- lua_gettable(lua, 3);
+ lua_gettable(lua, 2);
infile = (xm_io_file_t *)lua_touserdata(lua, -1);
lua_pop(lua, 1);
}
@@ -108,7 +108,7 @@ tb_int_t xm_process_open(lua_State *lua) {
// get outfile
if (!outpath) {
lua_pushstring(lua, "outfile");
- lua_gettable(lua, 3);
+ lua_gettable(lua, 2);
outfile = (xm_io_file_t *)lua_touserdata(lua, -1);
lua_pop(lua, 1);
}
@@ -116,7 +116,7 @@ tb_int_t xm_process_open(lua_State *lua) {
// get errfile
if (!errpath) {
lua_pushstring(lua, "errfile");
- lua_gettable(lua, 3);
+ lua_gettable(lua, 2);
errfile = (xm_io_file_t *)lua_touserdata(lua, -1);
lua_pop(lua, 1);
}
@@ -124,7 +124,7 @@ tb_int_t xm_process_open(lua_State *lua) {
// get inpipe
if (!inpath && !infile) {
lua_pushstring(lua, "inpipe");
- lua_gettable(lua, 3);
+ lua_gettable(lua, 2);
inpipe = (tb_pipe_file_ref_t)lua_touserdata(lua, -1);
lua_pop(lua, 1);
}
@@ -132,7 +132,7 @@ tb_int_t xm_process_open(lua_State *lua) {
// get outpipe
if (!outpath && !outfile) {
lua_pushstring(lua, "outpipe");
- lua_gettable(lua, 3);
+ lua_gettable(lua, 2);
outpipe = (tb_pipe_file_ref_t)lua_touserdata(lua, -1);
lua_pop(lua, 1);
}
@@ -140,7 +140,7 @@ tb_int_t xm_process_open(lua_State *lua) {
// get errpipe
if (!errpath && !errfile) {
lua_pushstring(lua, "errpipe");
- lua_gettable(lua, 3);
+ lua_gettable(lua, 2);
errpipe = (tb_pipe_file_ref_t)lua_touserdata(lua, -1);
lua_pop(lua, 1);
}
diff --git a/core/src/xmake/semver/select.c b/core/src/xmake/semver/select.c
index 283afb050..e95840eab 100644
--- a/core/src/xmake/semver/select.c
+++ b/core/src/xmake/semver/select.c
@@ -33,6 +33,69 @@
/* //////////////////////////////////////////////////////////////////////////////////////
* private implementation
*/
+static tb_char_t const *xm_semver_skip_version_prefix(tb_char_t const *version_str, tb_size_t *version_len) {
+ if (*version_len && (version_str[0] == 'v' || version_str[0] == '=')) {
+ ++version_str;
+ --*version_len;
+ }
+ return version_str;
+}
+static tb_bool_t xm_semver_is_exact_version(
+ tb_char_t const *version_str, tb_size_t version_len, tb_bool_t *is_exact_with_build) {
+ *is_exact_with_build = tb_false;
+ version_str = xm_semver_skip_version_prefix(version_str, &version_len);
+ semver_t version = { 0 };
+ if (!semvern(&version, version_str, version_len)) {
+ *is_exact_with_build = version.build.len > 0;
+ semver_dtor(&version);
+ return tb_true;
+ }
+ return tb_false;
+}
+static tb_long_t xm_semver_compare_build(semver_id_t const *left, semver_id_t const *right) {
+ while (left && right && left->len && right->len) {
+ if (left->numeric && right->numeric) {
+ if (left->num != right->num) {
+ return left->num > right->num ? 1 : -1;
+ }
+ } else {
+ tb_size_t size = left->len < right->len ? left->len : right->len;
+ tb_long_t result = tb_memcmp(left->raw, right->raw, size);
+ if (result) {
+ return result;
+ }
+ if (left->len != right->len) {
+ return left->len > right->len ? 1 : -1;
+ }
+ }
+ left = left->next;
+ right = right->next;
+ }
+ if (left && left->len) {
+ return 1;
+ }
+ if (right && right->len) {
+ return -1;
+ }
+ return 0;
+}
+static tb_long_t xm_semver_compare_with_build(semver_t const *left, semver_t const *right) {
+ tb_long_t result = semver_pcmp(left, right);
+ // xmake-repo orders build metadata as package revisions
+ return result ? result : xm_semver_compare_build(&left->build, &right->build);
+}
+static semver_t const *xm_semvers_find_newest(semvers_t const *versions) {
+ tb_assert_and_check_return_val(versions && versions->length, tb_null);
+
+ semver_t const *newest = &versions->data[0];
+ tb_size_t i = 0;
+ for (i = 1; i < versions->length; ++i) {
+ if (xm_semver_compare_with_build(&versions->data[i], newest) > 0) {
+ newest = &versions->data[i];
+ }
+ }
+ return newest;
+}
static tb_bool_t xm_semver_select_from_versions_tags1(
lua_State *lua, tb_int_t fromidx, semver_t *semver, semver_range_t const *range, semvers_t *matches) {
// clear matches
@@ -59,27 +122,24 @@ static tb_bool_t xm_semver_select_from_versions_tags1(
// no matches?
tb_check_return_val(matches->length, tb_false);
- // sort matches
- semvers_psort(matches);
-
// get the newest version
- semver_t top = semvers_ppop(matches);
+ semver_t const *top = xm_semvers_find_newest(matches);
lua_createtable(lua, 0, 2);
// return results
- lua_pushstring(lua, top.raw);
+ lua_pushstring(lua, top->raw);
lua_setfield(lua, -2, "version");
lua_pushstring(lua, fromidx == 2 ? "version" : "tag");
lua_setfield(lua, -2, "source");
- // exit the popped semver
- semver_dtor(&top);
-
return tb_true;
}
static tb_bool_t xm_semver_select_from_versions_tags2(
- lua_State *lua, tb_int_t fromidx, semver_t *semver, tb_char_t const *version_str, tb_size_t version_len) {
+ lua_State *lua, tb_int_t fromidx, tb_char_t const *version_str, tb_size_t version_len, tb_bool_t is_exact) {
+ if (is_exact) {
+ version_str = xm_semver_skip_version_prefix(version_str, &version_len);
+ }
lua_Integer i = 0;
luaL_checktype(lua, fromidx, LUA_TTABLE);
for (i = lua_objlen(lua, fromidx); i > 0; --i) {
@@ -88,8 +148,14 @@ static tb_bool_t xm_semver_select_from_versions_tags2(
tb_char_t const *source_str = luaL_checkstring(lua, -1);
tb_size_t source_len = tb_strlen(source_str);
+ tb_size_t source_version_len = source_len;
+ tb_char_t const *source_version_str = source_str;
+ // ignore a leading v/= prefix when comparing exact versions
+ if (is_exact) {
+ source_version_str = xm_semver_skip_version_prefix(source_str, &source_version_len);
+ }
lua_pop(lua, 1);
- if (source_len == version_len && tb_strncmp(source_str, version_str, version_len) == 0) {
+ if (source_version_len == version_len && tb_strncmp(source_version_str, version_str, version_len) == 0) {
lua_createtable(lua, 0, 2);
lua_pushlstring(lua, source_str, source_len);
lua_setfield(lua, -2, "version");
@@ -147,21 +213,17 @@ static tb_bool_t xm_semver_select_latest_from_versions_tags(lua_State *lua,
}
tb_check_return_val(matches->length, tb_false);
- // sort matches
- semvers_psort(matches);
-
// get the newest match
- semver_t top = semvers_ppop(matches);
+ semver_t const *top = xm_semvers_find_newest(matches);
lua_createtable(lua, 0, 2);
// return results
- lua_pushstring(lua, top.raw);
+ lua_pushstring(lua, top->raw);
lua_setfield(lua, -2, "version");
lua_pushstring(lua, fromidx == 2 ? "version" : "tag");
lua_setfield(lua, -2, "source");
- semver_dtor(&top);
return tb_true;
}
@@ -179,6 +241,8 @@ tb_int_t xm_semver_select(lua_State *lua) {
// select version
tb_bool_t ok = tb_false;
tb_bool_t is_range = tb_false;
+ tb_bool_t is_exact = tb_false;
+ tb_bool_t is_exact_with_build = tb_false;
tb_char_t const *range_str = tb_null;
semver_t semver = { 0 };
semvers_t matches = { 0 };
@@ -193,27 +257,27 @@ tb_int_t xm_semver_select(lua_State *lua) {
// parse the version range string
is_range = semver_rangen(&range, range_str, range_len) == 0;
- if (is_range) {
- // attempt to select version from the versions list first
- if (xm_semver_select_from_versions_tags1(lua, 2, &semver, &range, &matches)) {
+ is_exact = xm_semver_is_exact_version(range_str, range_len, &is_exact_with_build);
+
+ // matching order: versions exact -> tags exact -> versions range -> tags range
+ if (is_exact || !is_range) {
+ if (xm_semver_select_from_versions_tags2(lua, 2, range_str, range_len, is_exact)) {
ok = tb_true;
break;
}
-
- // attempt to select version from the tags list
- if (xm_semver_select_from_versions_tags1(lua, 3, &semver, &range, &matches)) {
+ if (xm_semver_select_from_versions_tags2(lua, 3, range_str, range_len, is_exact)) {
ok = tb_true;
break;
}
- } else {
- // attempt to select version from the versions list first
- if (xm_semver_select_from_versions_tags2(lua, 2, &semver, range_str, range_len)) {
+ }
+
+ // a build-qualified exact version identifies a specific package revision
+ if (is_range && !is_exact_with_build) {
+ if (xm_semver_select_from_versions_tags1(lua, 2, &semver, &range, &matches)) {
ok = tb_true;
break;
}
-
- // attempt to select version from the tags list
- if (xm_semver_select_from_versions_tags2(lua, 3, &semver, range_str, range_len)) {
+ if (xm_semver_select_from_versions_tags1(lua, 3, &semver, &range, &matches)) {
ok = tb_true;
break;
}
diff --git a/core/src/xmake/utf8/width.c b/core/src/xmake/utf8/width.c
index 615572462..fb79ee198 100644
--- a/core/src/xmake/utf8/width.c
+++ b/core/src/xmake/utf8/width.c
@@ -33,7 +33,11 @@
* utf8.width(codepoint)
*/
tb_int_t xm_utf8_width(lua_State* lua) {
- if (lua_isnumber(lua, 1)) {
+ /* we must not use lua_isnumber() here: it also accepts a numeric string,
+ * so utf8.width("9") would return the width of the code point 9 (a tab)
+ * instead of the width of the string "9"
+ */
+ if (lua_type(lua, 1) == LUA_TNUMBER) {
xm_utf8_int_t val = (xm_utf8_int_t)lua_tointeger(lua, 1);
lua_pushinteger(lua, xm_utf8_charwidth(val));
} else {
diff --git a/core/xmake.lua b/core/xmake.lua
index 97d19c568..5536cbf98 100644
--- a/core/xmake.lua
+++ b/core/xmake.lua
@@ -1,7 +1,7 @@
set_project("xmake")
set_xmakever("3.0.5")
set_policy("build.progress_style", "multirow")
-set_version("3.0.9", {build = "%Y%m%d"})
+set_version("3.1.1", {build = "%Y%m%d"})
-- set all warnings as errors
set_warnings("all", "error")
diff --git a/core/xmake.sh b/core/xmake.sh
index 31c5f8aea..935a7158b 100755
--- a/core/xmake.sh
+++ b/core/xmake.sh
@@ -1,7 +1,7 @@
#!/bin/sh
set_project "xmake"
-set_version "3.0.9" "%Y%m%d"
+set_version "3.1.1" "%Y%m%d"
# set warning all
set_warnings "all"
diff --git a/scripts/get.ps1 b/scripts/get.ps1
index 99ad1edba..487675c21 100755
--- a/scripts/get.ps1
+++ b/scripts/get.ps1
@@ -11,7 +11,7 @@ param (
)
& {
- $LastRelease = "v3.0.9"
+ $LastRelease = "v3.1.1"
$ErrorActionPreference = 'Stop'
function writeErrorTip($msg) {
diff --git a/scripts/rpmbuild/xmake.spec b/scripts/rpmbuild/xmake.spec
index df59e545a..fd4695904 100644
--- a/scripts/rpmbuild/xmake.spec
+++ b/scripts/rpmbuild/xmake.spec
@@ -3,7 +3,7 @@
%undefine __brp_mangle_shebangs
Name: xmake
-Version: 3.0.9
+Version: 3.1.1
Release: 1%{?dist}
Summary: A cross-platform build utility based on Lua
diff --git a/tests/actions/addon/custom-include/addon.lua b/tests/actions/addon/custom-include/addon.lua
new file mode 100644
index 000000000..71d01b5c2
--- /dev/null
+++ b/tests/actions/addon/custom-include/addon.lua
@@ -0,0 +1,2 @@
+addon("custom-include")
+ set_description("the addon which provides a custom includes file")
diff --git a/tests/actions/addon/custom-include/includes/check/xmake.lua b/tests/actions/addon/custom-include/includes/check/xmake.lua
new file mode 100644
index 000000000..953f7ba73
--- /dev/null
+++ b/tests/actions/addon/custom-include/includes/check/xmake.lua
@@ -0,0 +1,4 @@
+option("myoption")
+ set_default(true)
+ set_description("The option of the custom-include addon.")
+print("custom-include: includes check is loaded")
diff --git a/tests/actions/addon/custom-module/addon.lua b/tests/actions/addon/custom-module/addon.lua
new file mode 100644
index 000000000..604dbc645
--- /dev/null
+++ b/tests/actions/addon/custom-module/addon.lua
@@ -0,0 +1,2 @@
+addon("custom-module")
+ set_description("the addon which provides a custom module")
diff --git a/tests/actions/addon/custom-module/modules/greeting.lua b/tests/actions/addon/custom-module/modules/greeting.lua
new file mode 100644
index 000000000..843dddfc2
--- /dev/null
+++ b/tests/actions/addon/custom-module/modules/greeting.lua
@@ -0,0 +1,3 @@
+function main(name)
+ return "hello from custom-module: " .. name
+end
diff --git a/tests/actions/addon/custom-override/addon.lua b/tests/actions/addon/custom-override/addon.lua
new file mode 100644
index 000000000..3540b51f5
--- /dev/null
+++ b/tests/actions/addon/custom-override/addon.lua
@@ -0,0 +1,2 @@
+addon("custom-override")
+ set_description("the addon which takes over a builtin plugin")
diff --git a/tests/actions/addon/custom-override/plugins/format/main.lua b/tests/actions/addon/custom-override/plugins/format/main.lua
new file mode 100644
index 000000000..92f9b73b8
--- /dev/null
+++ b/tests/actions/addon/custom-override/plugins/format/main.lua
@@ -0,0 +1,3 @@
+function main()
+ print("hello from custom-override")
+end
diff --git a/tests/actions/addon/custom-override/plugins/format/xmake.lua b/tests/actions/addon/custom-override/plugins/format/xmake.lua
new file mode 100644
index 000000000..446e24db1
--- /dev/null
+++ b/tests/actions/addon/custom-override/plugins/format/xmake.lua
@@ -0,0 +1,6 @@
+-- @note `format` is a builtin plugin of xmake, we use it to test that an addon
+-- is able to take over a builtin plugin
+task("format")
+ set_category("plugin")
+ set_menu {usage = "xmake format", description = "Say hello instead of formatting.", options = {}}
+ on_run("main")
diff --git a/tests/actions/addon/custom-package/addon.lua b/tests/actions/addon/custom-package/addon.lua
new file mode 100644
index 000000000..56d8300f3
--- /dev/null
+++ b/tests/actions/addon/custom-package/addon.lua
@@ -0,0 +1,2 @@
+addon("custom-package")
+ set_description("the addon which provides a custom package")
diff --git a/tests/actions/addon/custom-package/includes/packages/src/include/hello_utils.h b/tests/actions/addon/custom-package/includes/packages/src/include/hello_utils.h
new file mode 100644
index 000000000..ca969acd2
--- /dev/null
+++ b/tests/actions/addon/custom-package/includes/packages/src/include/hello_utils.h
@@ -0,0 +1,6 @@
+#pragma once
+#include <stdio.h>
+
+static inline void hello_utils_greeting(void) {
+ printf("hello from the custom package!\n");
+}
diff --git a/tests/actions/addon/custom-package/includes/packages/xmake.lua b/tests/actions/addon/custom-package/includes/packages/xmake.lua
new file mode 100644
index 000000000..5a02187fd
--- /dev/null
+++ b/tests/actions/addon/custom-package/includes/packages/xmake.lua
@@ -0,0 +1,13 @@
+-- an addon can ship the package definitions, so that a project only needs
+-- includes("@addon/custom-package/packages") and add_requires("hello-utils")
+package("hello-utils")
+ set_kind("library", {headeronly = true})
+ set_description("the custom package of the tests")
+
+ -- the sources are shipped by this addon, so it can be installed offline
+ set_sourcedir(path.join(os.scriptdir(), "src"))
+
+ on_install(function (package)
+ os.cp("include", package:installdir())
+ end)
+package_end()
diff --git a/tests/actions/addon/custom-plugin/addon.lua b/tests/actions/addon/custom-plugin/addon.lua
new file mode 100644
index 000000000..ba4515c78
--- /dev/null
+++ b/tests/actions/addon/custom-plugin/addon.lua
@@ -0,0 +1,2 @@
+addon("custom-plugin")
+ set_description("the addon which provides a custom plugin")
diff --git a/tests/actions/addon/custom-plugin/plugins/hello_addon/main.lua b/tests/actions/addon/custom-plugin/plugins/hello_addon/main.lua
new file mode 100644
index 000000000..0cf3a0386
--- /dev/null
+++ b/tests/actions/addon/custom-plugin/plugins/hello_addon/main.lua
@@ -0,0 +1,5 @@
+import("core.base.option")
+
+function main()
+ print("hello from custom-plugin: %s", option.get("name") or "world")
+end
diff --git a/tests/actions/addon/custom-plugin/plugins/hello_addon/xmake.lua b/tests/actions/addon/custom-plugin/plugins/hello_addon/xmake.lua
new file mode 100644
index 000000000..7976a1d0e
--- /dev/null
+++ b/tests/actions/addon/custom-plugin/plugins/hello_addon/xmake.lua
@@ -0,0 +1,5 @@
+task("hello_addon")
+ set_category("plugin")
+ set_menu {usage = "xmake hello_addon", description = "Say hello from the addon.",
+ options = {{'n', "name", "kv", nil, "Set the name."}}}
+ on_run("main")
diff --git a/tests/actions/addon/custom-rule/addon.lua b/tests/actions/addon/custom-rule/addon.lua
new file mode 100644
index 000000000..2e9b68ab5
--- /dev/null
+++ b/tests/actions/addon/custom-rule/addon.lua
@@ -0,0 +1,2 @@
+addon("custom-rule")
+ set_description("the addon which provides a custom rule")
diff --git a/tests/actions/addon/custom-rule/modules/greeting.lua b/tests/actions/addon/custom-rule/modules/greeting.lua
new file mode 100644
index 000000000..f247d21ca
--- /dev/null
+++ b/tests/actions/addon/custom-rule/modules/greeting.lua
@@ -0,0 +1,3 @@
+function main(name)
+ return "hello from custom-rule: " .. name
+end
diff --git a/tests/actions/addon/custom-rule/rules/hello/xmake.lua b/tests/actions/addon/custom-rule/rules/hello/xmake.lua
new file mode 100644
index 000000000..07638387d
--- /dev/null
+++ b/tests/actions/addon/custom-rule/rules/hello/xmake.lua
@@ -0,0 +1,6 @@
+rule("hello")
+ on_load(function (target)
+ -- the addon code refers to its own resources with `@self`
+ import("@self.greeting")
+ print("custom-rule: %s", greeting(target:name()))
+ end)
diff --git a/tests/actions/addon/custom-template/addon.lua b/tests/actions/addon/custom-template/addon.lua
new file mode 100644
index 000000000..f01eda9c9
--- /dev/null
+++ b/tests/actions/addon/custom-template/addon.lua
@@ -0,0 +1,2 @@
+addon("custom-template")
+ set_description("the addon which provides a custom project template")
diff --git a/tests/actions/addon/custom-template/templates/c/customaddon/hello/src/main.c b/tests/actions/addon/custom-template/templates/c/customaddon/hello/src/main.c
new file mode 100644
index 000000000..115a2a792
--- /dev/null
+++ b/tests/actions/addon/custom-template/templates/c/customaddon/hello/src/main.c
@@ -0,0 +1,6 @@
+#include <stdio.h>
+
+int main(int argc, char** argv) {
+ printf("hello from the custom-template addon!\n");
+ return 0;
+}
diff --git a/tests/actions/addon/custom-template/templates/c/customaddon/hello/xmake.lua b/tests/actions/addon/custom-template/templates/c/customaddon/hello/xmake.lua
new file mode 100644
index 000000000..33c36ec7e
--- /dev/null
+++ b/tests/actions/addon/custom-template/templates/c/customaddon/hello/xmake.lua
@@ -0,0 +1,5 @@
+add_rules("mode.debug", "mode.release")
+
+target("${TARGET_NAME}")
+ set_kind("binary")
+ add_files("src/*.c")
diff --git a/tests/actions/addon/custom-toolchain/addon.lua b/tests/actions/addon/custom-toolchain/addon.lua
new file mode 100644
index 000000000..8537b3272
--- /dev/null
+++ b/tests/actions/addon/custom-toolchain/addon.lua
@@ -0,0 +1,6 @@
+addon("custom-toolchain")
+ set_description("the addon which provides a custom toolchain")
+ set_sourcedir("src")
+ -- the tool modules are imported with their plain names by the internal calls,
+ -- e.g. find_tool("mycl6x"), import("core.tools.mycl6x")
+ add_globalmodules("core.tools.mycl6x", "detect.tools.find_mycl6x")
diff --git a/tests/actions/addon/custom-toolchain/src/modules/core/tools/mycl6x.lua b/tests/actions/addon/custom-toolchain/src/modules/core/tools/mycl6x.lua
new file mode 100644
index 000000000..972242c98
--- /dev/null
+++ b/tests/actions/addon/custom-toolchain/src/modules/core/tools/mycl6x.lua
@@ -0,0 +1,12 @@
+-- the tool module of the my-c6000 toolchain
+--
+-- @note our compiler behaves like gcc, it's just a wrapper of the host one, so we only
+-- inherit it here, a real one implements the tool interfaces itself, e.g. `init`,
+-- `nf_define`, `compargv`, `link`, @see tests/apis/custom_toolchain
+--
+inherit("core.tools.gcc")
+
+-- init it
+function init(self)
+ self:set("cxflags", "-DMY_C6000_TOOL")
+end
diff --git a/tests/actions/addon/custom-toolchain/src/modules/detect/tools/find_mycl6x.lua b/tests/actions/addon/custom-toolchain/src/modules/detect/tools/find_mycl6x.lua
new file mode 100644
index 000000000..c0ed37914
--- /dev/null
+++ b/tests/actions/addon/custom-toolchain/src/modules/detect/tools/find_mycl6x.lua
@@ -0,0 +1,15 @@
+import("lib.detect.find_program")
+
+-- find the compiler of the my-c6000 toolchain
+--
+-- @note it's just the host compiler, we only need a real program to build with,
+-- the point of this test is that this finder itself comes from the addon
+--
+function main(opt)
+ for _, name in ipairs({"gcc", "clang", "cc"}) do
+ local program = find_program(name, opt)
+ if program then
+ return program
+ end
+ end
+end
diff --git a/tests/actions/addon/custom-toolchain/src/toolchains/my-c6000/xmake.lua b/tests/actions/addon/custom-toolchain/src/toolchains/my-c6000/xmake.lua
new file mode 100644
index 000000000..9b023bac7
--- /dev/null
+++ b/tests/actions/addon/custom-toolchain/src/toolchains/my-c6000/xmake.lua
@@ -0,0 +1,19 @@
+-- a custom toolchain distributed as an addon
+--
+-- @see tests/apis/custom_toolchain for the same toolchain maintained inside a project,
+-- distributing it as an addon is the recommended way
+--
+toolchain("my-c6000")
+ set_kind("standalone")
+ set_description("the custom toolchain of the tests")
+
+ set_toolset("cc", "mycl6x")
+ set_toolset("ld", "mycl6x")
+
+ on_check(function (toolchain)
+ return import("lib.detect.find_tool")("mycl6x")
+ end)
+
+ on_load(function (toolchain)
+ toolchain:add("cxflags", "-DMY_C6000")
+ end)
diff --git a/tests/actions/addon/projects/autofetch-badname/xmake.lua b/tests/actions/addon/projects/autofetch-badname/xmake.lua
new file mode 100644
index 000000000..ffbf6cbe4
--- /dev/null
+++ b/tests/actions/addon/projects/autofetch-badname/xmake.lua
@@ -0,0 +1,5 @@
+-- the `addon` name is reserved for the addon references
+add_addons("addon")
+
+target("test")
+ set_kind("phony")
diff --git a/tests/actions/addon/projects/autofetch-build/src/main.c b/tests/actions/addon/projects/autofetch-build/src/main.c
new file mode 100644
index 000000000..43509bfeb
--- /dev/null
+++ b/tests/actions/addon/projects/autofetch-build/src/main.c
@@ -0,0 +1,14 @@
+// the addons must provide the option and the toolchain of this project
+#ifndef MYOPTION
+# error the option of the custom-include addon is not found!
+#endif
+#ifndef MY_C6000
+# error the toolchain of the custom-toolchain addon is not used!
+#endif
+#ifndef MY_C6000_TOOL
+# error the tool module of the custom-toolchain addon is not used!
+#endif
+
+int main(int argc, char** argv) {
+ return 0;
+}
diff --git a/tests/actions/addon/projects/autofetch-build/xmake.lua b/tests/actions/addon/projects/autofetch-build/xmake.lua
new file mode 100644
index 000000000..862ddf676
--- /dev/null
+++ b/tests/actions/addon/projects/autofetch-build/xmake.lua
@@ -0,0 +1,14 @@
+-- the addons which this project needs, they are installed automatically
+add_addons("custom-include", "custom-rule", "custom-toolchain")
+
+-- the option comes from the includes file of an addon
+includes("@addon/custom-include/check")
+
+target("hello")
+ set_kind("binary")
+ add_files("src/main.c")
+ add_rules("@addon/custom-rule/hello")
+ set_toolchains("@addon/custom-toolchain/my-c6000")
+ if has_config("myoption") then
+ add_defines("MYOPTION")
+ end
diff --git a/tests/actions/addon/projects/autofetch-lockmiss/xmake-addons.lock b/tests/actions/addon/projects/autofetch-lockmiss/xmake-addons.lock
new file mode 100644
index 000000000..750a8e609
--- /dev/null
+++ b/tests/actions/addon/projects/autofetch-lockmiss/xmake-addons.lock
@@ -0,0 +1,9 @@
+{
+ __meta__ = {
+ version = "1.0"
+ },
+ -- this version is not installed, we only install `latest` from the local directory
+ ["custom-include"] = {
+ version = "v9.9.9"
+ }
+}
diff --git a/tests/actions/addon/projects/autofetch-lockmiss/xmake.lua b/tests/actions/addon/projects/autofetch-lockmiss/xmake.lua
new file mode 100644
index 000000000..a96a4e713
--- /dev/null
+++ b/tests/actions/addon/projects/autofetch-lockmiss/xmake.lua
@@ -0,0 +1,4 @@
+add_addons("custom-include")
+
+target("test")
+ set_kind("phony")
diff --git a/tests/actions/addon/projects/autofetch/xmake.lua b/tests/actions/addon/projects/autofetch/xmake.lua
new file mode 100644
index 000000000..75c909904
--- /dev/null
+++ b/tests/actions/addon/projects/autofetch/xmake.lua
@@ -0,0 +1,8 @@
+-- the addons which this project needs, they are installed automatically
+add_addons("custom-include")
+
+-- the addon is installed automatically, so we can use its includes file here
+includes("@addon/custom-include/check")
+
+target("test")
+ set_kind("phony")
diff --git a/tests/actions/addon/projects/custom-package/src/main.c b/tests/actions/addon/projects/custom-package/src/main.c
new file mode 100644
index 000000000..12208c880
--- /dev/null
+++ b/tests/actions/addon/projects/custom-package/src/main.c
@@ -0,0 +1,6 @@
+#include <hello_utils.h>
+
+int main(int argc, char** argv) {
+ hello_utils_greeting();
+ return 0;
+}
diff --git a/tests/actions/addon/projects/custom-package/xmake.lua b/tests/actions/addon/projects/custom-package/xmake.lua
new file mode 100644
index 000000000..1c967020d
--- /dev/null
+++ b/tests/actions/addon/projects/custom-package/xmake.lua
@@ -0,0 +1,9 @@
+-- the package definitions come from the addon
+includes("@addon/custom-package/packages")
+
+add_requires("hello-utils")
+
+target("test")
+ set_kind("binary")
+ add_files("src/main.c")
+ add_packages("hello-utils")
diff --git a/tests/actions/addon/test.lua b/tests/actions/addon/test.lua
new file mode 100644
index 000000000..fcc5c2efc
--- /dev/null
+++ b/tests/actions/addon/test.lua
@@ -0,0 +1,507 @@
+import("core.base.global")
+import("lib.detect.find_program")
+
+-- the addon fixtures, each of them provides one kind of payload only
+--
+-- @note they are independent from each other, @see tests/actions/addon/custom-*
+
+function _addondir(name)
+ return path.join(os.scriptdir(), name)
+end
+
+function _remove(name)
+ try { function () os.runv("xmake", {"addon", "--remove", "--force", name}) end }
+end
+
+-- install the given addon fixture from its local directory
+function _install(name)
+ _remove(name)
+ os.runv("xmake", {"addon", "--install", _addondir(name)})
+end
+
+-- run the given function with the addons installed, we always remove them again
+function _with_addons(names, func)
+ for _, name in ipairs(names) do
+ _install(name)
+ end
+ try
+ {
+ func,
+ finally
+ {
+ -- @note try() swallows the errors if we do not re-raise them here
+ function (ok, errors)
+ for _, name in ipairs(names) do
+ _remove(name)
+ end
+ if not ok then
+ raise(errors)
+ end
+ end
+ }
+ }
+end
+
+-- create a temporary repository which indexes the given addon fixtures, and register it
+--
+-- @note the recipes only point at the fixtures with `set_sourcedir`, we need not generate any payload
+function _with_repo(recipes, func)
+
+ -- @note installing from a repository goes through the package manager, and it always
+ -- needs git, which cannot be installed on the hosts without a package manager,
+ -- e.g. dragonflybsd
+ if not find_program("git") then
+ print("git not found, we skip the repository tests!")
+ return
+ end
+
+ local suffix = path.filename(os.tmpfile()):gsub("[^%w]", "")
+ local reponame = "addon-test-repo-" .. suffix
+ local repodir = os.tmpfile() .. ".addon-repo"
+ for name, body in pairs(recipes) do
+ io.writefile(path.join(repodir, "addons", name:sub(1, 1), name, "xmake.lua"),
+ ("package(%q)\n set_kind(\"addon\")\n set_description(\"the addon fixture of the tests\")\n %s\n"):format(name, body))
+ end
+
+ -- register the repository and clear the quick search cache, it will be rebuilt on the next search
+ local cachefile = path.join(global.cachedir(), "repository")
+ local cache = os.isfile(cachefile) and io.load(cachefile) or {}
+ cache.repositories = cache.repositories or {}
+
+ -- a killed test run may leave its temporary repository registered, and a dangling
+ -- repository breaks every following xrepo command, so we drop them here
+ for name, dirs in pairs(cache.repositories) do
+ if name:startswith("addon-test-repo-") and not os.isdir(dirs[1]) then
+ cache.repositories[name] = nil
+ end
+ end
+ cache.repositories[reponame] = {repodir}
+ io.save(cachefile, cache)
+ os.tryrm(path.join(global.cachedir(), "quick_search"))
+
+ try
+ {
+ function ()
+ func(reponame)
+ end,
+ finally
+ {
+ -- @note try() swallows the errors if we do not re-raise them here
+ function (ok, errors)
+ for name, _ in pairs(recipes) do
+ _remove(name)
+ end
+ local cache = os.isfile(cachefile) and io.load(cachefile) or {}
+ if cache.repositories then
+ cache.repositories[reponame] = nil
+ end
+ io.save(cachefile, cache)
+ os.tryrm(path.join(global.cachedir(), "quick_search"))
+ os.tryrm(repodir)
+ if not ok then
+ raise(errors)
+ end
+ end
+ }
+ }
+end
+
+-- run the given command in a temporary project and return its output
+function _run_project(content, argv, opt)
+ opt = opt or {}
+ local projectdir = os.tmpfile() .. ".addon-project"
+ os.tryrm(projectdir)
+ io.writefile(path.join(projectdir, "xmake.lua"), content)
+ for file, filecontent in pairs(opt.files or {}) do
+ io.writefile(path.join(projectdir, file), filecontent)
+ end
+ local oldir = os.cd(projectdir)
+ local out, errors
+ try
+ {
+ function () out = os.iorunv("xmake", argv) end,
+ catch { function (e) errors = e end },
+ finally
+ {
+ function ()
+ os.cd(oldir)
+ os.tryrm(projectdir)
+ end
+ }
+ }
+ if errors then
+ raise(errors)
+ end
+ return out
+end
+
+function _config_project(content)
+ return _run_project(content, {"config", "-y"})
+end
+
+-- copy the given fixture project to a temporary directory and run the given function in it
+--
+-- @note we cannot run them in place, they would generate the lock and the build files,
+-- @see tests/actions/addon/projects
+--
+function _with_project(name, func)
+ local projectdir = os.tmpfile() .. ".addon-project"
+ os.tryrm(projectdir)
+ os.mkdir(projectdir)
+ os.cp(path.join(os.scriptdir(), "projects", name, "*"), projectdir)
+ local oldir = os.cd(projectdir)
+ try
+ {
+ function ()
+ func(projectdir)
+ end,
+ finally
+ {
+ -- @note try() swallows the errors if we do not re-raise them here
+ function (ok, errors)
+ os.cd(oldir)
+ os.tryrm(projectdir)
+ if not ok then
+ raise(errors)
+ end
+ end
+ }
+ }
+end
+
+-- only the payloads should be installed, our own files should not
+function test_install(t)
+ _with_addons({"custom-toolchain"}, function ()
+
+ -- it has the `src` layout, e.g. set_sourcedir("src")
+ local installdir = path.join(global.directory(), "addons", "custom-toolchain", "latest")
+ t:require(os.isdir(path.join(installdir, "toolchains")))
+ t:require(os.isdir(path.join(installdir, "modules")))
+ for _, ourfile in ipairs({"src", "addon.lua"}) do
+ t:require_not(os.exists(path.join(installdir, ourfile)))
+ end
+
+ -- the addon describes itself, we should get its description from the manifest
+ t:require(os.iorunv("xmake", {"addon", "--list"}):find("the addon which provides a custom toolchain", 1, true))
+ end)
+end
+
+-- an addon can provide a plugin, e.g. xmake hello_addon
+function test_plugin(t)
+ _with_addons({"custom-plugin"}, function ()
+ t:require(os.iorunv("xmake", {"hello_addon", "-n", "xmake"}):find("hello from custom-plugin: xmake", 1, true))
+ end)
+
+ -- it should not be runnable after removing it
+ t:require_not(try { function () os.runv("xmake", {"hello_addon"}); return true end })
+end
+
+-- an addon can provide a rule, e.g. add_rules("@addon/custom-rule/hello")
+function test_rule(t)
+ local projectfile = [[
+target("test")
+ set_kind("phony")
+ add_rules("@addon/custom-rule/hello")
+]]
+ _with_addons({"custom-rule"}, function ()
+ -- the rule imports a module of its own addon with `@self`
+ t:require(_config_project(projectfile):find("custom-rule: hello from custom-rule: test", 1, true))
+ end)
+
+ -- it should fail if the addon which provides it is not installed
+ t:require_not(try { function () _config_project(projectfile); return true end })
+end
+
+-- an addon can provide a module, e.g. import("@addon.custom-module.greeting")
+function test_module(t)
+ _with_addons({"custom-module"}, function ()
+ local script = "import(\"@addon.custom-module.greeting\"); print(greeting(\"xmake\"))"
+ t:require(os.iorunv("xmake", {"lua", "-c", script}):find("hello from custom-module: xmake", 1, true))
+
+ -- the addon modules are namespaced, they cannot be imported with their plain names
+ t:require_not(try { function () os.runv("xmake", {"lua", "-c", "import(\"greeting\")"}); return true end })
+ end)
+end
+
+-- an addon can provide an includes file, e.g. includes("@addon/custom-include/check")
+function test_include(t)
+ _with_addons({"custom-include"}, function ()
+ local out = _config_project([[
+includes("@addon/custom-include/check")
+target("test")
+ set_kind("phony")
+]])
+ t:require(out:find("custom-include: includes check is loaded", 1, true))
+ end)
+end
+
+-- an addon can provide a project template, e.g. xmake create -t customaddon.hello
+function test_template(t)
+ _with_addons({"custom-template"}, function ()
+ t:require(os.iorunv("xmake", {"create", "--list", "-l", "c"}):find("customaddon.hello", 1, true))
+
+ local projectdir = os.tmpfile() .. ".addon-project"
+ os.tryrm(projectdir)
+ os.runv("xmake", {"create", "-l", "c", "-t", "customaddon.hello", "-P", projectdir})
+ t:require(os.isfile(path.join(projectdir, "src", "main.c")))
+ os.tryrm(projectdir)
+ end)
+end
+
+-- an addon can ship the package definitions in its includes file, so that a project
+-- only needs `add_requires`, @see tests/actions/addon/custom-package
+function test_include_packages(t)
+
+ -- @note we need a compiler here, the project links against the package
+ if not (find_program("gcc") or find_program("clang") or find_program("cc")) then
+ return
+ end
+ _with_addons({"custom-package"}, function ()
+ _with_project("custom-package", function ()
+ t:require(os.iorunv("xmake", {"build", "-y"}):find("build ok", 1, true))
+ t:require(os.iorunv("xmake", {"run"}):find("hello from the custom package", 1, true))
+ end)
+ end)
+end
+
+-- an addon can provide a toolchain and its tool modules
+--
+-- @see tests/apis/custom_toolchain for the same toolchain maintained inside a project
+function test_toolchain(t)
+ _with_addons({"custom-toolchain"}, function ()
+
+ -- the toolchain can be loaded with the addon name
+ local script = "import(\"core.tool.toolchain\"); print(toolchain.load(\"@addon/custom-toolchain/my-c6000\"):get(\"description\"))"
+ t:require(os.iorunv("xmake", {"lua", "-c", script}):find("the custom toolchain of the tests", 1, true))
+
+ -- its tool modules are exported by the manifest, so the internal calls can import them
+ -- with their plain names, e.g. add_globalmodules("core.tools.mycl6x")
+ --
+ -- @note there is no builtin `mycl6x`, so they can only come from this addon
+ local script2 = "import(\"core.tools.mycl6x\"); assert(mycl6x.compargv, \"invalid tool module!\"); " ..
+ "import(\"lib.detect.find_tool\"); assert(find_tool(\"mycl6x\"), \"mycl6x not found!\")"
+ os.runv("xmake", {"lua", "-c", script2})
+
+ -- and a project can build with it, the flags of the toolchain and of its tool module
+ -- are both checked by the source file
+ --
+ -- @note its compiler is just the host one, so we can only build with it if there is one
+ if find_program("gcc") or find_program("clang") or find_program("cc") then
+ _run_project([[
+target("test")
+ set_kind("binary")
+ set_toolchains("@addon/custom-toolchain/my-c6000")
+ add_files("src/main.c")
+]], {"build", "-y"}, {files = {["src/main.c"] = [[
+#ifndef MY_C6000
+# error the flags of the addon toolchain are not used!
+#endif
+#ifndef MY_C6000_TOOL
+# error the flags of its tool module are not used!
+#endif
+int main(int argc, char** argv) { return 0; }
+]]}})
+ end
+ end)
+end
+
+-- the addons which a project declares with `add_addons` are installed automatically
+--
+-- @note they must be installed before loading the project, it may use their includes files
+function test_autofetch(t)
+ local recipes = {["custom-include"] = ("set_sourcedir(%q)"):format(_addondir("custom-include"))}
+ _with_repo(recipes, function ()
+ _remove("custom-include")
+ _with_project("autofetch", function (projectdir)
+
+ -- it should be installed when loading the project, so that its includes file can be found,
+ -- and we should tell the user why we install something, it may need to confirm and download
+ local output = os.iorunv("xmake", {"config", "-y"})
+ t:require(output:find("custom-include: includes check is loaded", 1, true))
+ t:require(output:find("this project needs the addons", 1, true))
+
+ -- and it should be locked
+ local lockfile = path.join(projectdir, "xmake-addons.lock")
+ t:require(os.isfile(lockfile))
+ t:require(io.load(lockfile)["custom-include"] ~= nil)
+
+ -- we should not install it again
+ t:require_not(os.iorunv("xmake", {"config", "-y"}):find("install custom-include", 1, true))
+ end)
+ end)
+end
+
+-- every command builds the option menu, which merges the project tasks in a best-effort way,
+-- so the commands which need not the project should never install its addons
+function test_autofetch_skipped_for_option_menu(t)
+ local recipes = {["custom-include"] = ("set_sourcedir(%q)"):format(_addondir("custom-include"))}
+ _with_repo(recipes, function ()
+ _remove("custom-include")
+ _with_project("autofetch", function (projectdir)
+ os.runv("xmake", {"addon", "--list"})
+ os.runv("xmake", {"lua", "-c", "print(\"hello\")"})
+ t:require_not(os.isfile(path.join(projectdir, "xmake-addons.lock")))
+ end)
+ end)
+end
+
+-- a complete project which declares its addons with `add_addons` and builds with them,
+-- @see tests/actions/addon/projects/autofetch-build
+function test_autofetch_build(t)
+
+ -- @note the compiler of the custom toolchain is just the host one,
+ -- so we can only build it if there is one
+ if not (find_program("gcc") or find_program("clang") or find_program("cc")) then
+ return
+ end
+
+ local names = {"custom-include", "custom-rule", "custom-toolchain"}
+ local recipes = {}
+ for _, name in ipairs(names) do
+ recipes[name] = ("set_sourcedir(%q)"):format(_addondir(name))
+ end
+ _with_repo(recipes, function ()
+ for _, name in ipairs(names) do
+ _remove(name)
+ end
+ _with_project("autofetch-build", function (projectdir)
+
+ -- all of them should be installed when loading the project, and it should build
+ -- with their includes file, rule and toolchain, @see src/main.c
+ local output = os.iorunv("xmake", {"build", "-y"})
+ t:require(output:find("custom-include: includes check is loaded", 1, true))
+ t:require(output:find("custom-rule: hello from custom-rule: hello", 1, true))
+ t:require(output:find("build ok", 1, true))
+
+ -- and all of them should be locked
+ local lockinfo = io.load(path.join(projectdir, "xmake-addons.lock"))
+ for _, name in ipairs(names) do
+ t:require(lockinfo[name] ~= nil)
+ end
+ end)
+ end)
+end
+
+-- a locked version which is not installed must not hide the addon
+--
+-- @note the auto-fetch installs the locked version, but the commands which do not load
+-- the project content only pin it, e.g. `xmake lua`, @see project._pin_addons()
+--
+function test_autofetch_lock_missing_version(t)
+ _with_addons({"custom-include"}, function ()
+ _with_project("autofetch-lockmiss", function ()
+ local script = "import(\"core.package.addon\"); print(addon.addons()[\"custom-include\"] ~= nil)"
+ t:require(os.iorunv("xmake", {"lua", "-c", script}):find("true", 1, true))
+ end)
+ end)
+end
+
+-- the declared addons are checked, e.g. the reserved names
+function test_autofetch_invalid(t)
+ _with_project("autofetch-badname", function ()
+ t:require_not(try { function () os.runv("xmake", {"config", "-y"}); return true end })
+ end)
+end
+
+-- the addons can be installed from a repository, by plain name and by repo@name
+function test_install_from_repo(t)
+ local recipes = {["custom-plugin"] = ("set_sourcedir(%q)"):format(_addondir("custom-plugin")),
+ ["custom-module"] = ("set_sourcedir(%q)"):format(_addondir("custom-module"))}
+ _with_repo(recipes, function (reponame)
+ os.runv("xmake", {"addon", "--install", "-y", "custom-plugin"})
+ t:require(os.iorunv("xmake", {"hello_addon"}):find("hello from custom-plugin", 1, true))
+
+ os.runv("xmake", {"addon", "--remove", "custom-plugin"})
+ os.runv("xmake", {"addon", "--install", "-y", reponame .. "@custom-plugin"})
+ t:require(os.iorunv("xmake", {"hello_addon"}):find("hello from custom-plugin", 1, true))
+
+ -- it should be searchable, and the addons should not be found by the package search
+ --
+ -- @note we cannot run the `xrepo` program here, it may not be in the PATH, e.g. on the ci,
+ -- and `xrepo search` is just a wrapper of it
+ --
+ t:require(os.iorunv("xmake", {"addon", "--search", "custom-plugin"}):find("custom-plugin", 1, true))
+ t:require_not(os.iorunv("xmake", {"lua", "private.xrepo", "search", "custom-plugin"}):find("custom-plugin", 1, true))
+
+ -- and several of them can be installed in one shot, they are resolved and installed together
+ os.runv("xmake", {"addon", "--remove", "--force", "custom-plugin"})
+ os.runv("xmake", {"addon", "--install", "-y", "custom-plugin", reponame .. "@custom-module"})
+ t:require(os.iorunv("xmake", {"hello_addon"}):find("hello from custom-plugin", 1, true))
+ t:require(os.iorunv("xmake", {"lua", "-c", "import(\"@addon.custom-module.greeting\"); print(greeting(\"xmake\"))"})
+ :find("hello from custom-module: xmake", 1, true))
+ end)
+end
+
+-- an addon can depend on the other addons, they are installed and activated together
+function test_addon_deps(t)
+ local recipes = {
+ ["custom-module"] = ("set_sourcedir(%q)"):format(_addondir("custom-module")),
+ ["custom-plugin"] = ("set_sourcedir(%q)\n add_deps(\"custom-module\", {kind = \"addon\"})"):format(_addondir("custom-plugin"))
+ }
+ _with_repo(recipes, function ()
+ os.runv("xmake", {"addon", "--install", "-y", "custom-plugin"})
+ local out = os.iorunv("xmake", {"addon", "--list"})
+ t:require(out:find("custom-plugin", 1, true))
+ t:require(out:find("custom-module", 1, true))
+
+ -- we cannot remove the dependency, it's depended on by the other addon
+ t:require_not(try { function () os.runv("xmake", {"addon", "--remove", "custom-module"}); return true end })
+ end)
+end
+
+-- the plugins and the templates are not namespaced, the conflicts should be rejected when installing
+function test_install_conflicts(t)
+ _with_addons({"custom-plugin"}, function ()
+
+ -- this addon provides the same plugin name
+ local clonedir = os.tmpfile() .. ".addon-clone"
+ os.tryrm(clonedir)
+ io.writefile(path.join(clonedir, "addon.lua"), "addon(\"custom-plugin-clone\")\n")
+ io.writefile(path.join(clonedir, "plugins", "hello_addon", "xmake.lua"),
+ "task(\"hello_addon\")\n set_category(\"plugin\")\n set_menu {}\n on_run(function () end)\n")
+ t:require_not(try { function () os.runv("xmake", {"addon", "--install", "-y", clonedir}); return true end })
+ os.tryrm(clonedir)
+
+ -- and the other commands should still work
+ t:require(os.iorunv("xmake", {"hello_addon"}):find("hello from custom-plugin", 1, true))
+ end)
+end
+
+-- an addon is able to take over a builtin plugin, so we can move a deprecated builtin
+-- plugin to an addon, e.g. `xmake format`
+function test_install_override(t)
+
+ -- the builtin plugin is used if we do not install the addon
+ t:require_not(os.iorunv("xmake", {"format", "--help"}):find("Say hello instead of formatting", 1, true))
+
+ _with_addons({"custom-override"}, function ()
+ local out = os.iorunv("xmake", {"format"})
+ t:require(out:find("hello from custom-override", 1, true))
+
+ -- and it should not be reported as a conflict
+ t:require_not(out:find("conflicts", 1, true))
+ t:require(os.iorunv("xmake", {"format", "--help"}):find("Say hello instead of formatting", 1, true))
+ end)
+
+ -- the builtin plugin is used again after removing the addon
+ t:require_not(os.iorunv("xmake", {"format", "--help"}):find("Say hello instead of formatting", 1, true))
+end
+
+-- the invalid installs should fail, and the `addon` name is reserved for the addon references
+function test_invalid(t)
+ t:require_not(try { function () os.runv("xmake", {"addon", "--install", "-y", "addon-test-missing"}); return true end })
+ t:require_not(try { function () os.runv("xmake", {"addon", "--install", "-y", "somerepo@.."}); return true end })
+
+ -- the addon name in its manifest must match the package name which distributes it
+ local recipes = {["custom-plugin-badname"] = ("set_sourcedir(%q)"):format(_addondir("custom-plugin"))}
+ _with_repo(recipes, function ()
+ t:require_not(try { function () os.runv("xmake", {"addon", "--install", "-y", "custom-plugin-badname"}); return true end })
+ end)
+
+ -- the `addon` name is reserved
+ t:require_not(try { function () _config_project([[
+add_requires("addon")
+target("test")
+ set_kind("phony")
+]]); return true end })
+end
diff --git a/tests/modules/binutils/test.lua b/tests/modules/binutils/test.lua
index ce68bcda8..fe4a182f2 100644
--- a/tests/modules/binutils/test.lua
+++ b/tests/modules/binutils/test.lua
@@ -81,6 +81,130 @@ function test_format(t)
os.tryrm(tempdir)
end
+function test_bin2elf(t)
+ local tempdir = "temp/binutils_bin2elf"
+ os.tryrm(tempdir)
+ os.mkdir(tempdir)
+
+ -- input binary payload
+ local input = path.join(tempdir, "data.bin")
+ io.writefile(input, "hello world payload", {encoding = "binary"})
+
+ -- parse the ELF header fields we care about (class, data encoding, machine, flags)
+ local function _parse_elf_header(objectfile)
+ local data = io.readfile(objectfile, {encoding = "binary"})
+ t:require(data ~= nil and #data >= 24)
+ -- magic
+ t:are_equal(string.byte(data, 1), 0x7f)
+ t:are_equal(string.byte(data, 2), string.byte("E"))
+ t:are_equal(string.byte(data, 3), string.byte("L"))
+ t:are_equal(string.byte(data, 4), string.byte("F"))
+ local class = string.byte(data, 5) -- e_ident[EI_CLASS]: 1 = 32-bit, 2 = 64-bit
+ local encode = string.byte(data, 6) -- e_ident[EI_DATA]: 1 = LSB, 2 = MSB
+ local bigendian = (encode == 2)
+ -- read a target-endian integer of nbytes at 1-based index
+ local function _readint(index, nbytes)
+ local value = 0
+ for i = 0, nbytes - 1 do
+ local byte = string.byte(data, index + i)
+ if bigendian then
+ value = value * 256 + byte
+ else
+ value = value + byte * (256 ^ i)
+ end
+ end
+ return value
+ end
+ local machine = _readint(19, 2) -- e_machine at offset 18
+ -- e_flags: offset 36 for 32-bit, 48 for 64-bit
+ local flags = (class == 2) and _readint(49, 4) or _readint(37, 4)
+ return {class = class, encode = encode, machine = machine, flags = flags}
+ end
+
+ local function _gen(arch)
+ local objectfile = path.join(tempdir, arch .. ".o")
+ binutils.bin2obj(input, objectfile, {format = "elf", arch = arch, basename = "test"})
+ return _parse_elf_header(objectfile)
+ end
+
+ -- loong64: 64-bit class, LoongArch machine (issue: was wrongly detected as 32-bit)
+ local loong64 = _gen("loong64")
+ t:are_equal(loong64.class, 2)
+ t:are_equal(loong64.encode, 1)
+ t:are_equal(loong64.machine, 0x102)
+ t:are_equal(loong64.flags, 0x43) -- double-float ABI + object ABI v1
+
+ -- riscv64: double-float ABI in e_flags (issue: was soft-float, e_flags == 0)
+ local riscv64 = _gen("riscv64")
+ t:are_equal(riscv64.class, 2)
+ t:are_equal(riscv64.machine, 0xf3)
+ t:are_equal(riscv64.flags, 0x5) -- RVC + double-float
+
+ -- riscv (32-bit): same machine/flags, 32-bit class
+ local riscv = _gen("riscv")
+ t:are_equal(riscv.class, 1)
+ t:are_equal(riscv.machine, 0xf3)
+ t:are_equal(riscv.flags, 0x5)
+
+ -- s390x: big-endian (issue: was wrongly little-endian)
+ local s390x = _gen("s390x")
+ t:are_equal(s390x.class, 2)
+ t:are_equal(s390x.encode, 2) -- MSB
+ t:are_equal(s390x.machine, 0x16)
+
+ -- x86_64: unchanged, little-endian 64-bit
+ local x86_64 = _gen("x86_64")
+ t:are_equal(x86_64.class, 2)
+ t:are_equal(x86_64.encode, 1)
+ t:are_equal(x86_64.machine, 0x3e)
+ t:are_equal(x86_64.flags, 0)
+
+ -- mips is big-endian, mipsel is little-endian
+ -- 32-bit variants use the o32 ABI + CPIC (e_flags == 0x1004)
+ local mips = _gen("mips")
+ t:are_equal(mips.encode, 2)
+ t:are_equal(mips.machine, 0x08)
+ t:are_equal(mips.flags, 0x1004)
+ local mipsel = _gen("mipsel")
+ t:are_equal(mipsel.encode, 1)
+ t:are_equal(mipsel.machine, 0x08)
+ t:are_equal(mipsel.flags, 0x1004)
+
+ -- 64-bit variants use the n64 ABI (implied by ELFCLASS64) + CPIC (e_flags == 0x4)
+ local mips64 = _gen("mips64")
+ t:are_equal(mips64.class, 2)
+ t:are_equal(mips64.encode, 2)
+ t:are_equal(mips64.machine, 0x08)
+ t:are_equal(mips64.flags, 0x4)
+ local mips64el = _gen("mips64el")
+ t:are_equal(mips64el.class, 2)
+ t:are_equal(mips64el.encode, 1)
+ t:are_equal(mips64el.machine, 0x08)
+ t:are_equal(mips64el.flags, 0x4)
+
+ -- xmake has no separate ppc64le arch (find_platform maps powerpc64le -> ppc64) and
+ -- ppc64le is the dominant modern target, so a plain "ppc64" is treated as ppc64le:
+ -- little-endian + OpenPOWER ELFv2 ABI (e_flags low bits == 2)
+ local ppc64 = _gen("ppc64")
+ t:are_equal(ppc64.encode, 1)
+ t:are_equal(ppc64.machine, 0x15)
+ t:are_equal(ppc64.flags, 0x2)
+
+ -- explicit ppc64le behaves the same
+ local ppc64le = _gen("ppc64le")
+ t:are_equal(ppc64le.encode, 1)
+ t:are_equal(ppc64le.machine, 0x15)
+ t:are_equal(ppc64le.flags, 0x2)
+
+ -- explicit big-endian ppc64be keeps the ELFv1 ABI
+ local ppc64be = _gen("ppc64be")
+ t:are_equal(ppc64be.encode, 2)
+ t:are_equal(ppc64be.machine, 0x15)
+ t:are_equal(ppc64be.flags, 0x1)
+
+ os.tryrm(tempdir)
+end
+
function test_deplibs(t)
local tempdir = "temp/binutils_deplibs"
os.tryrm(tempdir)
diff --git a/tests/modules/project_depend/test.lua b/tests/modules/project_depend/test.lua
new file mode 100644
index 000000000..2df79ba16
--- /dev/null
+++ b/tests/modules/project_depend/test.lua
@@ -0,0 +1,19 @@
+import("core.project.depend")
+
+function test_nested_values_append(t)
+ local dependinfo = {values = {{"clang", "-m64"}}}
+ local changed = depend.is_changed(dependinfo, {values = {{"clang", "-m64", "-DFEATURE_ON"}}})
+ t:require(changed)
+end
+
+function test_nested_values_remove(t)
+ local dependinfo = {values = {{"clang", "-m64", "-DFEATURE_ON"}}}
+ local changed = depend.is_changed(dependinfo, {values = {{"clang", "-m64"}}})
+ t:require(changed)
+end
+
+function test_nested_values_unchanged(t)
+ local dependinfo = {values = {{"clang", "-m64", "-DFEATURE_ON"}}}
+ local changed = depend.is_changed(dependinfo, {values = {{"clang", "-m64", "-DFEATURE_ON"}}})
+ t:require_not(changed)
+end
diff --git a/tests/modules/semver/test.lua b/tests/modules/semver/test.lua
index 1ab6dc8aa..57d213f4a 100755
--- a/tests/modules/semver/test.lua
+++ b/tests/modules/semver/test.lua
@@ -7,6 +7,12 @@ function _check_semver_select(t, results, required_ver, versions, tags, branches
t:are_equal(source, results[2])
end
+function _check_semver_select_failed(t, required_ver, versions, tags, branches)
+ t:will_raise(function()
+ semver.select(required_ver, versions or {}, tags or {}, branches or {})
+ end, "unable to select version")
+end
+
-- test select version
function test_semver_select(t)
@@ -18,15 +24,101 @@ function test_semver_select(t)
, "^1.5.0"
,{"1.4.0", "1.5.0", "1.5.1"})
+ _check_semver_select(t, {"3.53.0+200", "version"}
+ , "3.53.0+200"
+ , {"3.53.0+0", "3.53.0+100", "3.53.0+200"})
+
+ _check_semver_select_failed(t, "3.53.0+999", {"3.53.0+100"})
+
+ _check_semver_select(t, {"3.53.0+200", "version"}
+ , "3.53.0"
+ , {"3.53.0+0", "3.53.0+100", "3.53.0+200"})
+
+ _check_semver_select(t, {"3.53.0+200", "version"}
+ , "3.53.0"
+ , {"3.53.0+200", "3.53.0+100", "3.53.0+0"})
+
+ _check_semver_select(t, {"3.53.0+beta", "version"}
+ , "3.53.0"
+ , {"3.53.0+alpha", "3.53.0+beta"})
+
+ _check_semver_select(t, {"3.53.0+beta", "version"}
+ , "3.53.0"
+ , {"3.53.0+beta", "3.53.0+alpha"})
+
+ _check_semver_select(t, {"1.2.3", "version"}
+ , "1.2.3"
+ , {"1.2.3+1", "1.2.3"})
+
+ _check_semver_select(t, {"1.2.3", "version"}
+ , "=1.2.3"
+ , {"1.2.3+7", "1.2.3"})
+
+ _check_semver_select(t, {"1.2.9", "version"}
+ , "1.2"
+ , {"1.2", "1.2.9"})
+
+ _check_semver_select(t, {"1.9.0", "version"}
+ , "^1.2.3"
+ , {"^1.2.3", "1.2.3", "1.9.0"})
+
+ _check_semver_select(t, {"1.2.3", "tag"}
+ , "1.2.3"
+ , {"v1.2.3+7"}
+ , {"1.2.3"})
+
+ _check_semver_select(t, {"v1.2.3+7", "version"}
+ , "1.2.3+7"
+ , {"v1.2.3+8", "v1.2.3+7"})
+
+ _check_semver_select(t, {"1.2.3+7", "version"}
+ , "v1.2.3+7"
+ , {"1.2.3+8", "1.2.3+7"})
+
+ _check_semver_select(t, {"3.53.0+200", "tag"}
+ , "3.53.0+200"
+ , nil
+ , {"3.53.0+0", "3.53.0+200"})
+
+ _check_semver_select_failed(t, "3.53.0+999", nil, {"3.53.0+100"})
+
+ _check_semver_select(t, {"v1.2.3+7", "tag"}
+ , "=1.2.3+7"
+ , nil
+ , {"v1.2.3+7", "v1.2.3+8"})
+
+ _check_semver_select(t, {"v1.2.3+7", "tag"}
+ , "1.2.3+7"
+ , {"1.2.3+8"}
+ , {"v1.2.3+7"})
+
_check_semver_select(t, {"master", "branch"}
, "master"
, {"1.4.0", "1.5.0", "1.5.1"}
, {"v1.2.0", "v1.6.0"}
, {"master", "dev"})
+ _check_semver_select(t, {"next", "branch"}
+ , "next"
+ , nil
+ , {"vnext"}
+ , {"next"})
+
_check_semver_select(t, {"1.5.1", "version"}
, "latest"
, {"1.4.0", "1.5.0", "1.5.1"})
+
+ _check_semver_select(t, {"1.0.0+10", "version"}
+ , "latest"
+ , {"1.0.0+9", "1.0.0+10"})
+
+ _check_semver_select(t, {"1.0.0+rev.10", "version"}
+ , "latest"
+ , {"1.0.0+rev.9", "1.0.0+rev.10"})
+
+ _check_semver_select(t, {"3.53.0+200", "version"}
+ , "latest"
+ , {"3.53.0+0", "3.53.0+200", "3.53.0+100"})
end
-- select version
diff --git a/tests/projects/c++/modules/precompile_reduced_bmi/src/base.mpp b/tests/projects/c++/modules/precompile_reduced_bmi/src/base.mpp
new file mode 100644
index 000000000..e3e6224f2
--- /dev/null
+++ b/tests/projects/c++/modules/precompile_reduced_bmi/src/base.mpp
@@ -0,0 +1,5 @@
+export module base;
+
+export int base_value() {
+ return 40;
+}
diff --git a/tests/projects/c++/modules/precompile_reduced_bmi/src/hello.mpp b/tests/projects/c++/modules/precompile_reduced_bmi/src/hello.mpp
new file mode 100644
index 000000000..9de491171
--- /dev/null
+++ b/tests/projects/c++/modules/precompile_reduced_bmi/src/hello.mpp
@@ -0,0 +1,7 @@
+export module hello;
+
+import base;
+
+export int answer() {
+ return base_value() + 2;
+}
diff --git a/tests/projects/c++/modules/precompile_reduced_bmi/src/main.cpp b/tests/projects/c++/modules/precompile_reduced_bmi/src/main.cpp
new file mode 100644
index 000000000..75adbd883
--- /dev/null
+++ b/tests/projects/c++/modules/precompile_reduced_bmi/src/main.cpp
@@ -0,0 +1,5 @@
+import hello;
+
+int main() {
+ return answer() == 42 ? 0 : 1;
+}
diff --git a/tests/projects/c++/modules/precompile_reduced_bmi/test.lua b/tests/projects/c++/modules/precompile_reduced_bmi/test.lua
new file mode 100644
index 000000000..765290f7b
--- /dev/null
+++ b/tests/projects/c++/modules/precompile_reduced_bmi/test.lua
@@ -0,0 +1,107 @@
+inherit(".test_base")
+import("core.base.json")
+
+local _CLANG_MIN_VER = "23"
+
+function clang_min_ver()
+ return _CLANG_MIN_VER
+end
+
+function _check_commands(config, outdata)
+ local has_precompile_reduced_bmi = outdata:find("--precompile-reduced-bmi", 1, true) ~= nil
+ local expect_precompile_reduced_bmi = config.precompile_reduced_bmi and config.two_phases
+ assert(has_precompile_reduced_bmi == expect_precompile_reduced_bmi,
+ "unexpected --precompile-reduced-bmi usage\n%s", outdata)
+
+ if config.two_phases then
+ local object_command
+ for _, line in ipairs(outdata:split("\n", {plain = true})) do
+ if line:find("hello.mpp", 1, true) and
+ (line:find("hello.mpp.o", 1, true) or line:find("hello.mpp.obj", 1, true)) and
+ line:find(" -c ", 1, true) and
+ not line:find("clang-scan-deps", 1, true) and
+ not line:find("--precompile", 1, true) then
+ object_command = line
+ break
+ end
+ end
+ assert(object_command, "module object command missing\n%s", outdata)
+ local consumes_bmi = object_command:find("hello.pcm", 1, true) ~= nil
+ assert(consumes_bmi ~= expect_precompile_reduced_bmi, "unexpected module object input\n%s", outdata)
+ end
+end
+
+function _check_incremental()
+ local outdata = os.iorun("xmake -v")
+ if outdata:find("compiling", 1, true) or
+ outdata:find("linking", 1, true) or
+ outdata:find("generating", 1, true) then
+ raise("Modules incremental compilation does not work\n%s", outdata)
+ end
+end
+
+function _check_compile_commands(config)
+ os.run("xmake project -k compile_commands")
+ local bmi_command
+ local object_command
+ for _, command in ipairs(assert(json.loadfile("compile_commands.json"))) do
+ if command.file:endswith("hello.mpp") then
+ local commandline = command.command or table.concat(command.arguments or {}, " ")
+ if commandline:find("--precompile", 1, true) then
+ bmi_command = commandline
+ elseif commandline:find("hello.mpp.o", 1, true) or commandline:find("hello.mpp.obj", 1, true) then
+ object_command = commandline
+ end
+ end
+ end
+ assert(bmi_command, "module BMI command missing from compile_commands.json")
+ assert(object_command, "module object command missing from compile_commands.json")
+ local expect_precompile_reduced_bmi = config.precompile_reduced_bmi and config.two_phases
+ assert((bmi_command:find("--precompile-reduced-bmi", 1, true) ~= nil) == expect_precompile_reduced_bmi,
+ "unexpected module BMI command\n%s", bmi_command)
+ assert(object_command:find("hello.mpp", 1, true) and not object_command:find("hello.pcm", 1, true),
+ "unexpected module object command\n%s", object_command)
+end
+
+function _configure(config)
+ local argv = {"f", "--yes", "--toolchain=" .. config.toolchain}
+ if config.platform then
+ table.join2(argv, {"-p", config.platform})
+ end
+ if config.runtimes then
+ table.insert(argv, "--runtimes=" .. config.runtimes)
+ end
+ local policies = "--policies=build.c++.modules.std:" .. (config.stdmodule and "y" or "n")
+ policies = policies .. ",build.c++.modules.fallbackscanner:" .. (config.fallbackscanner and "y" or "n")
+ policies = policies .. ",build.c++.modules.two_phases:" .. (config.two_phases and "y" or "n")
+ policies = policies .. ",build.c++.modules.clang.precompile_reduced_bmi:" .. (config.precompile_reduced_bmi and "y" or "n")
+ table.insert(argv, policies)
+ table.join2(argv, config.flags or {})
+ os.execv("xmake", argv)
+end
+
+function _check_build(config)
+ local outdata = os.iorun("xmake -rv")
+ _check_commands(config, outdata)
+
+ os.run("xmake run")
+ _check_incremental()
+ if config.two_phases then
+ _check_compile_commands(config)
+ end
+
+ if config.two_phases then
+ local toggled_config = table.clone(config)
+ toggled_config.precompile_reduced_bmi = not config.precompile_reduced_bmi
+ _configure(toggled_config)
+ local toggled_outdata = os.iorun("xmake -v")
+ _check_commands(toggled_config, toggled_outdata)
+ os.run("xmake run")
+ _check_incremental()
+ _check_compile_commands(toggled_config)
+ end
+end
+
+function main(_)
+ run_tests({compiler = "clang", version = clang_min_ver(), precompile_reduced_bmi = true, build = _check_build})
+end
diff --git a/tests/projects/c++/modules/precompile_reduced_bmi/xmake.lua b/tests/projects/c++/modules/precompile_reduced_bmi/xmake.lua
new file mode 100644
index 000000000..0aa2dc516
--- /dev/null
+++ b/tests/projects/c++/modules/precompile_reduced_bmi/xmake.lua
@@ -0,0 +1,6 @@
+add_rules("mode.release", "mode.debug")
+set_languages("c++20")
+
+target("precompile_reduced_bmi")
+ set_kind("binary")
+ add_files("src/*.cpp", "src/*.mpp")
diff --git a/tests/projects/c++/modules/stdmodules_deps/src/bar.mpp b/tests/projects/c++/modules/stdmodules_deps/src/bar.mpp
index 001cd7c2d..ab104076c 100644
--- a/tests/projects/c++/modules/stdmodules_deps/src/bar.mpp
+++ b/tests/projects/c++/modules/stdmodules_deps/src/bar.mpp
@@ -4,11 +4,9 @@ import std;
export auto my_sum2(std::size_t a, std::size_t b) -> std::size_t;
-#if defined(__GNUC__) && !defined(__clang__)
-inline
-#else
-module :private;
-#endif
- auto my_sum2(std::size_t a, std::size_t b) -> std::size_t {
+// Clang 23 implements P1857R3, which rejects module directives controlled by
+// preprocessing conditionals. This test therefore uses an inline definition.
+// See https://wg21.link/P1857R3
+inline auto my_sum2(std::size_t a, std::size_t b) -> std::size_t {
return a + a + b + b;
}
diff --git a/tests/projects/c++/modules/stdmodules_deps_ordering/src/foo.cpp b/tests/projects/c++/modules/stdmodules_deps_ordering/src/foo.cpp
new file mode 100644
index 000000000..5b9d1194e
--- /dev/null
+++ b/tests/projects/c++/modules/stdmodules_deps_ordering/src/foo.cpp
@@ -0,0 +1,5 @@
+module foo;
+
+auto my_sum(int a, int b) -> int {
+ return a + b;
+}
diff --git a/tests/projects/c++/modules/stdmodules_deps_ordering/src/foo.mpp b/tests/projects/c++/modules/stdmodules_deps_ordering/src/foo.mpp
new file mode 100644
index 000000000..4c9a649ca
--- /dev/null
+++ b/tests/projects/c++/modules/stdmodules_deps_ordering/src/foo.mpp
@@ -0,0 +1,3 @@
+export module foo;
+
+export auto my_sum(int a, int b) -> int;
diff --git a/tests/projects/c++/modules/stdmodules_deps_ordering/src/main.cpp b/tests/projects/c++/modules/stdmodules_deps_ordering/src/main.cpp
new file mode 100644
index 000000000..1f87ceb59
--- /dev/null
+++ b/tests/projects/c++/modules/stdmodules_deps_ordering/src/main.cpp
@@ -0,0 +1,8 @@
+import foo;
+
+import std;
+
+int main() {
+ std::cout << my_sum(1, 2) << std::endl;
+ return 0;
+}
diff --git a/tests/projects/c++/modules/stdmodules_deps_ordering/test.lua b/tests/projects/c++/modules/stdmodules_deps_ordering/test.lua
new file mode 100644
index 000000000..e77adc4ae
--- /dev/null
+++ b/tests/projects/c++/modules/stdmodules_deps_ordering/test.lua
@@ -0,0 +1 @@
+inherit(".test_stdmodules")
diff --git a/tests/projects/c++/modules/stdmodules_deps_ordering/xmake.lua b/tests/projects/c++/modules/stdmodules_deps_ordering/xmake.lua
new file mode 100644
index 000000000..09c3f6a94
--- /dev/null
+++ b/tests/projects/c++/modules/stdmodules_deps_ordering/xmake.lua
@@ -0,0 +1,19 @@
+add_rules("mode.debug", "mode.release")
+set_languages("c++latest")
+
+-- test that a plain c++ file is ordered after all the bmis it reuses from its deps,
+-- not only after the last one
+--
+-- @note foo must not import std, otherwise its bmi and the std bmi
+-- would not be built by two distinct jobs of the foo target
+--
+-- @see https://github.com/xmake-io/xmake/issues/7690
+target("foo")
+ set_kind("static")
+ add_files("src/foo.cpp")
+ add_files("src/foo.mpp", {public = true})
+
+target("main")
+ set_kind("binary")
+ add_deps("foo")
+ add_files("src/main.cpp")
diff --git a/tests/projects/c++/modules/test_base.lua b/tests/projects/c++/modules/test_base.lua
index 372229695..2d0fa3f57 100644
--- a/tests/projects/c++/modules/test_base.lua
+++ b/tests/projects/c++/modules/test_base.lua
@@ -103,6 +103,9 @@ function build_tests(toolchain_name, opt)
local policies = "--policies=build.c++.modules.std:" .. (opt.stdmodule and "y" or "n")
policies = policies .. ",build.c++.modules.fallbackscanner:" .. (opt.fallbackscanner and "y" or "n")
policies = policies .. ",build.c++.modules.two_phases:" .. (two_phases and "y" or "n")
+ if opt.precompile_reduced_bmi ~= nil then
+ policies = policies .. ",build.c++.modules.clang.precompile_reduced_bmi:" .. (opt.precompile_reduced_bmi and "y" or "n")
+ end
local platform = " "
if opt.platform then
@@ -127,7 +130,7 @@ function build_tests(toolchain_name, opt)
os.exec("xmake clean -a")
os.exec("xmake f" .. platform .. "--toolchain=" .. toolchain_name .. runtimes .. "-c --yes " .. policies .. flags)
if opt.build then
- opt.build()
+ opt.build(table.join(opt, {toolchain = toolchain_name, two_phases = two_phases}))
else
_build(opt.check_outdata)
end
@@ -150,9 +153,12 @@ function run_tests(clang_options, gcc_options, msvc_options)
if not clang_options.disable_clang_cl then
local clang_cl_options = table.clone(clang_options)
clang_cl_options.compiler = "clang-cl"
- clang_cl_options.version = clang_cl_min_ver()
+ local clang_cl_minver = clang_cl_min_ver()
+ if semver.compare(clang_cl_options.version, clang_cl_minver) < 0 then
+ clang_cl_options.version = clang_cl_minver
+ end
build_tests("clang-cl", clang_cl_options)
- build_tests("clang-cl", table.join(clang_options, {two_phases = false}))
+ build_tests("clang-cl", table.join(clang_cl_options, {two_phases = false}))
end
if clang_options.stdmodule then
wprint("std modules tests skipped for Windows clang libc++ as it's not currently supported officially")
diff --git a/tests/projects/embed/esp32/blink/boards/esp32c3/board.h b/tests/projects/embed/esp32/blink/boards/esp32c3/board.h
new file mode 100644
index 000000000..126f2c2c3
--- /dev/null
+++ b/tests/projects/embed/esp32/blink/boards/esp32c3/board.h
@@ -0,0 +1,7 @@
+// the board configuration of esp32c3, it's selected by the `board` option
+//
+// @note gpio12-17 are the spi flash of the esp32c3, do not use them
+//
+#pragma once
+
+#define LED_GPIO 8
diff --git a/tests/projects/embed/esp32/blink/src/main.c b/tests/projects/embed/esp32/blink/src/main.c
new file mode 100644
index 000000000..b51e3b67b
--- /dev/null
+++ b/tests/projects/embed/esp32/blink/src/main.c
@@ -0,0 +1,26 @@
+// blink the led of the board and print a heartbeat
+//
+// the led pin comes from `boards/<board>/board.h`, it's selected by the board option,
+// e.g. xmake f --board=esp32c3
+//
+// @note the entry is the esp-idf native `void app_main(void)`, and we print with
+// `esp_rom_printf`, it writes to the rom console, which is the usb port on the
+// boards booting over the native usb (usb-serial-jtag), e.g. xmake monitor
+//
+#include "board.h"
+#include "driver/gpio.h"
+#include "esp_rom_sys.h"
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+
+void app_main(void)
+{
+ esp_rom_printf("blink: toggle gpio %d every 500ms\n", LED_GPIO);
+ gpio_reset_pin(LED_GPIO);
+ gpio_set_direction(LED_GPIO, GPIO_MODE_OUTPUT);
+ for (int count = 0; ; count++) {
+ gpio_set_level(LED_GPIO, count & 1);
+ esp_rom_printf("blink: tick %d\n", count);
+ vTaskDelay(pdMS_TO_TICKS(500));
+ }
+}
diff --git a/tests/projects/embed/esp32/blink/xmake-addons.lua b/tests/projects/embed/esp32/blink/xmake-addons.lua
new file mode 100644
index 000000000..aea4ad480
--- /dev/null
+++ b/tests/projects/embed/esp32/blink/xmake-addons.lua
@@ -0,0 +1,2 @@
+-- the addon is installed automatically when we load this project
+add_addons("esp32-devel")
diff --git a/tests/projects/embed/esp32/blink/xmake.lua b/tests/projects/embed/esp32/blink/xmake.lua
new file mode 100644
index 000000000..3fea2dd12
--- /dev/null
+++ b/tests/projects/embed/esp32/blink/xmake.lua
@@ -0,0 +1,15 @@
+-- the esp32 blink example, it needs the esp32-devel addon
+--
+-- $ xmake f --board=esp32c3
+-- $ xmake
+-- $ xmake install -- flash the image to the board
+-- $ xmake run -- flash it and monitor the serial output
+--
+includes("@addon/esp32-devel/board")
+
+target("blink")
+ add_rules("@addon/esp32-devel/app")
+ add_files("src/*.c")
+
+ -- the led pin of the board, @see boards/<board>/board.h
+ add_includedirs("boards/" .. (get_config("board") or "esp32c3"))
diff --git a/tests/projects/other/bin2c/src/asset.bin b/tests/projects/other/bin2c/src/asset.bin
new file mode 100644
index 000000000..db1331f22
--- /dev/null
+++ b/tests/projects/other/bin2c/src/asset.bin
@@ -0,0 +1 @@
+hello transform! \ No newline at end of file
diff --git a/tests/projects/other/bin2c/src/asset2.bin b/tests/projects/other/bin2c/src/asset2.bin
new file mode 100644
index 000000000..d7ffc4b96
--- /dev/null
+++ b/tests/projects/other/bin2c/src/asset2.bin
@@ -0,0 +1 @@
+hello luafile! \ No newline at end of file
diff --git a/tests/projects/other/bin2c/src/main.c b/tests/projects/other/bin2c/src/main.c
index 74898132c..d85e9934e 100644
--- a/tests/projects/other/bin2c/src/main.c
+++ b/tests/projects/other/bin2c/src/main.c
@@ -1,6 +1,7 @@
#include <stdio.h>
#include <stdint.h>
#include <ctype.h>
+#include <string.h>
static unsigned char g_bin_data[] = {
#include "data.bin.h"
@@ -10,6 +11,14 @@ static unsigned char g_ico_data[] = {
#include "xmake.ico.h"
};
+static unsigned char g_asset_data[] = {
+ #include "asset.bin.h"
+};
+
+static unsigned char g_asset2_data[] = {
+ #include "asset2.bin.h"
+};
+
static void hexdump(const char* name, const uint8_t* data, uint32_t size) {
printf("%s: size: %u bytes\n", name, (unsigned int)size);
if (size == 0) {
@@ -94,5 +103,25 @@ int main(int argc, char** argv)
hexdump("data.bin", g_bin_data, (uint32_t)sizeof(g_bin_data));
printf("\n");
hexdump("xmake.ico", g_ico_data, (uint32_t)sizeof(g_ico_data));
+ printf("\n");
+ hexdump("asset.bin (transformed)", g_asset_data, (uint32_t)sizeof(g_asset_data));
+ printf("\n");
+ hexdump("asset2.bin (transformed by lua file)", g_asset2_data, (uint32_t)sizeof(g_asset2_data));
+
+ // verify the function transform: asset.bin must be the reverse of "hello transform!" (+ zeroend '\0')
+ const char* expected = "!mrofsnart olleh";
+ if (sizeof(g_asset_data) != 17 || memcmp(g_asset_data, expected, 16) != 0) {
+ printf("asset.bin: transform verification failed!\n");
+ return 1;
+ }
+ printf("asset.bin: transform verification ok\n");
+
+ // verify the lua-file transform: asset2.bin must be the reverse of "hello luafile!" (+ zeroend '\0')
+ const char* expected2 = "!elifaul olleh";
+ if (sizeof(g_asset2_data) != 15 || memcmp(g_asset2_data, expected2, 14) != 0) {
+ printf("asset2.bin: transform verification failed!\n");
+ return 1;
+ }
+ printf("asset2.bin: transform verification ok\n");
return 0;
}
diff --git a/tests/projects/other/bin2c/test.lua b/tests/projects/other/bin2c/test.lua
index b57362078..a23bb98a3 100644
--- a/tests/projects/other/bin2c/test.lua
+++ b/tests/projects/other/bin2c/test.lua
@@ -1,3 +1,5 @@
function main(t)
t:build()
+ -- run the target, main.c verifies the transformed asset.bin and exits non-zero on mismatch
+ os.exec("xmake run test")
end
diff --git a/tests/projects/other/bin2c/transform.lua b/tests/projects/other/bin2c/transform.lua
new file mode 100644
index 000000000..54cf1f394
--- /dev/null
+++ b/tests/projects/other/bin2c/transform.lua
@@ -0,0 +1,5 @@
+function main(inputfile, outputfile)
+ import("core.base.bytes")
+ local data = io.readfile(inputfile, {encoding = "binary"})
+ io.writefile(outputfile, data:reverse(), {encoding = "binary"})
+end
diff --git a/tests/projects/other/bin2c/xmake.lua b/tests/projects/other/bin2c/xmake.lua
index b00dedb65..d60aea710 100644
--- a/tests/projects/other/bin2c/xmake.lua
+++ b/tests/projects/other/bin2c/xmake.lua
@@ -4,7 +4,15 @@ target("test")
set_kind("binary")
add_rules("utils.bin2c", {linewidth = 16, extensions = {".bin", ".ico"}})
add_files("src/*.c")
- add_files("src/*.bin")
+ add_files("src/data.bin")
add_files("src/xmake.ico", {nozeroend = true})
+ add_files("src/asset.bin", {transform = function (inputfile, outputfile, opt)
+ import("core.base.bytes")
+ assert(bytes and opt.target)
+ local data = io.readfile(inputfile, {encoding = "binary"})
+ io.writefile(outputfile, data:reverse(), {encoding = "binary"})
+ end})
+ -- transform can also be a lua script file (supported by the project generators)
+ add_files("src/asset2.bin", {transform = path.join(os.projectdir(), "transform.lua")})
diff --git a/tests/projects/other/bin2obj/src/asset.bin b/tests/projects/other/bin2obj/src/asset.bin
new file mode 100644
index 000000000..db1331f22
--- /dev/null
+++ b/tests/projects/other/bin2obj/src/asset.bin
@@ -0,0 +1 @@
+hello transform! \ No newline at end of file
diff --git a/tests/projects/other/bin2obj/src/asset2.bin b/tests/projects/other/bin2obj/src/asset2.bin
new file mode 100644
index 000000000..d7ffc4b96
--- /dev/null
+++ b/tests/projects/other/bin2obj/src/asset2.bin
@@ -0,0 +1 @@
+hello luafile! \ No newline at end of file
diff --git a/tests/projects/other/bin2obj/src/main.c b/tests/projects/other/bin2obj/src/main.c
index bc1d2442f..03c50352d 100644
--- a/tests/projects/other/bin2obj/src/main.c
+++ b/tests/projects/other/bin2obj/src/main.c
@@ -1,6 +1,7 @@
#include <stdio.h>
#include <stdint.h>
#include <ctype.h>
+#include <string.h>
extern const uint8_t _binary_data_bin_start[];
extern const uint8_t _binary_data_bin_end[];
@@ -8,30 +9,39 @@ extern const uint8_t _binary_data_bin_end[];
extern const uint8_t _binary_xmake_ico_start[];
extern const uint8_t _binary_xmake_ico_end[];
+extern const uint8_t _binary_asset_bin_start[];
+extern const uint8_t _binary_asset_bin_end[];
+
+extern const uint8_t _binary_asset2_bin_start[];
+extern const uint8_t _binary_asset2_bin_end[];
+
+extern const uint8_t _binary_asset2_bin_start[];
+extern const uint8_t _binary_asset2_bin_end[];
+
static void hexdump(const char* name, const uint8_t* data, uint32_t size) {
printf("%s: size: %u bytes\n", name, (unsigned int)size);
if (size == 0) {
return;
}
-
+
// if file is larger than 128 bytes, only dump first 64 and last 64 bytes
uint32_t dump_size = size;
uint32_t start_offset = 0;
uint32_t end_offset = 0;
uint32_t skip_size = 0;
-
+
if (size > 128) {
dump_size = 64;
start_offset = 0;
end_offset = size - 64;
skip_size = end_offset - start_offset - dump_size;
}
-
+
// dump start
for (uint32_t offset = start_offset; offset < start_offset + dump_size; offset += 16) {
// print offset
printf("%08x ", (unsigned int)offset);
-
+
// print hex bytes (8 bytes, space, 8 bytes)
for (uint32_t i = 0; i < 16; i++) {
if (offset + i < size) {
@@ -43,7 +53,7 @@ static void hexdump(const char* name, const uint8_t* data, uint32_t size) {
printf(" ");
}
}
-
+
// print ASCII representation
printf(" |");
for (uint32_t i = 0; i < 16 && offset + i < size; i++) {
@@ -52,18 +62,18 @@ static void hexdump(const char* name, const uint8_t* data, uint32_t size) {
}
printf("|\n");
}
-
+
// print skip indicator if needed
if (skip_size > 0) {
printf(" ... (skipped %u bytes) ...\n", (unsigned int)skip_size);
}
-
+
// dump end if needed
if (size > 128) {
for (uint32_t offset = end_offset; offset < size; offset += 16) {
// print offset
printf("%08x ", (unsigned int)offset);
-
+
// print hex bytes (8 bytes, space, 8 bytes)
for (uint32_t i = 0; i < 16; i++) {
if (offset + i < size) {
@@ -75,7 +85,7 @@ static void hexdump(const char* name, const uint8_t* data, uint32_t size) {
printf(" ");
}
}
-
+
// print ASCII representation
printf(" |");
for (uint32_t i = 0; i < 16 && offset + i < size; i++) {
@@ -90,9 +100,31 @@ static void hexdump(const char* name, const uint8_t* data, uint32_t size) {
int main(int argc, char** argv) {
const uint32_t _binary_data_bin_size = (uint32_t)(_binary_data_bin_end - _binary_data_bin_start);
const uint32_t _binary_xmake_ico_size = (uint32_t)(_binary_xmake_ico_end - _binary_xmake_ico_start);
+ const uint32_t _binary_asset_bin_size = (uint32_t)(_binary_asset_bin_end - _binary_asset_bin_start);
+ const uint32_t _binary_asset2_bin_size = (uint32_t)(_binary_asset2_bin_end - _binary_asset2_bin_start);
hexdump("data.bin", _binary_data_bin_start, _binary_data_bin_size);
printf("\n");
hexdump("xmake.ico", _binary_xmake_ico_start, _binary_xmake_ico_size);
+ printf("\n");
+ hexdump("asset.bin (transformed)", _binary_asset_bin_start, _binary_asset_bin_size);
+ printf("\n");
+ hexdump("asset2.bin (transformed by lua file)", _binary_asset2_bin_start, _binary_asset2_bin_size);
+
+ // verify the function transform: asset.bin must be the reverse of "hello transform!" (+ zeroend '\0')
+ const char* expected = "!mrofsnart olleh";
+ if (_binary_asset_bin_size != 17 || memcmp(_binary_asset_bin_start, expected, 16) != 0) {
+ printf("asset.bin: transform verification failed!\n");
+ return 1;
+ }
+ printf("asset.bin: transform verification ok\n");
+
+ // verify the lua-file transform: asset2.bin must be the reverse of "hello luafile!" (+ zeroend '\0')
+ const char* expected2 = "!elifaul olleh";
+ if (_binary_asset2_bin_size != 15 || memcmp(_binary_asset2_bin_start, expected2, 14) != 0) {
+ printf("asset2.bin: transform verification failed!\n");
+ return 1;
+ }
+ printf("asset2.bin: transform verification ok\n");
return 0;
}
diff --git a/tests/projects/other/bin2obj/test.lua b/tests/projects/other/bin2obj/test.lua
index 7f2050b62..a75aa7e40 100644
--- a/tests/projects/other/bin2obj/test.lua
+++ b/tests/projects/other/bin2obj/test.lua
@@ -1,4 +1,6 @@
function main(t)
t:build()
+ -- run the target, main.c verifies the transformed asset.bin and exits non-zero on mismatch
+ os.exec("xmake run test")
end
diff --git a/tests/projects/other/bin2obj/transform.lua b/tests/projects/other/bin2obj/transform.lua
new file mode 100644
index 000000000..54cf1f394
--- /dev/null
+++ b/tests/projects/other/bin2obj/transform.lua
@@ -0,0 +1,5 @@
+function main(inputfile, outputfile)
+ import("core.base.bytes")
+ local data = io.readfile(inputfile, {encoding = "binary"})
+ io.writefile(outputfile, data:reverse(), {encoding = "binary"})
+end
diff --git a/tests/projects/other/bin2obj/xmake.lua b/tests/projects/other/bin2obj/xmake.lua
index 9541e17ae..d383bb1bb 100644
--- a/tests/projects/other/bin2obj/xmake.lua
+++ b/tests/projects/other/bin2obj/xmake.lua
@@ -6,3 +6,11 @@ target("test")
add_files("src/*.c")
add_files("src/data.bin", {zeroend = true})
add_files("src/xmake.ico", {zeroend = false})
+ add_files("src/asset.bin", {zeroend = true, transform = function (inputfile, outputfile, opt)
+ import("core.base.bytes")
+ assert(bytes and opt.target)
+ local data = io.readfile(inputfile, {encoding = "binary"})
+ io.writefile(outputfile, data:reverse(), {encoding = "binary"})
+ end})
+ -- transform can also be a lua script file (supported by the project generators)
+ add_files("src/asset2.bin", {zeroend = true, transform = path.join(os.projectdir(), "transform.lua")})
diff --git a/xmake/actions/addon/main.lua b/xmake/actions/addon/main.lua
new file mode 100644
index 000000000..99f92d125
--- /dev/null
+++ b/xmake/actions/addon/main.lua
@@ -0,0 +1,334 @@
+--!A cross-platform build utility based on Lua
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+-- Copyright (C) 2015-present, Xmake Open Source Community.
+--
+-- @author ruki
+-- @file main.lua
+--
+
+-- imports
+import("core.base.option")
+import("core.package.addon")
+import("core.project.project")
+import("devel.git")
+import("private.action.addon.impl.install_addons")
+import("private.action.addon.impl.xrepo", {alias = "xrepo_addon"})
+import("private.action.require.impl.environment")
+import("private.action.require.impl.search_packages")
+
+-- validate an addon directory name
+function _check_addon_name(name)
+ assert(type(name) == "string" and name ~= "" and name ~= "." and not name:find("..", 1, true) and not name:find("[/\\:]"), "invalid addon name(%s)!", name)
+ return name
+end
+
+-- get addon directory in ~/.xmake/addons
+function _get_addondir(name, version)
+ local addondir = addon.installdir()
+ if name then
+ addondir = path.join(addondir, addon.dirname(_check_addon_name(name)))
+ if version then
+ addondir = path.join(addondir, version)
+ end
+ end
+ return addondir
+end
+
+-- install the given addons from the repositories which provide them
+--
+-- @note we install them in one shot, xrepo resolves and installs them in parallel
+--
+function _install_from_repo(names)
+ for _, name in ipairs(names) do
+ -- e.g. myrepo@myaddon
+ _check_addon_name(name:split("@", {plain = true, limit = 2})[2] or name)
+ end
+ xrepo_addon("install", names, {force = option.get("force")})
+end
+
+-- install a single addon from a source directory (as the given name, default to the directory name)
+function _install_from_local(dir, name)
+ assert(os.isdir(dir), "addon path(%s) not found!", dir)
+
+ -- we need to normalize it first, e.g. `/path/to/myaddon/` -> `/path/to/myaddon`,
+ -- otherwise we cannot get the addon name from the directory
+ dir = path.normalize(path.absolute(dir))
+
+ -- we only install the payload directories, the addon repository may have its own files,
+ -- e.g. tests, ci scripts and documents, and they can also be placed in the `src` subdirectory
+ local payloadroot = addon.payloadroot(dir)
+ assert(payloadroot, "addon path(%s): no payload directory found, e.g. ${bright}plugins${clear}!", dir)
+
+ -- the addon describes itself? its manifest is always authoritative,
+ -- otherwise we can only guess its name from the directory name
+ local manifest = addon.manifest(dir)
+ if manifest then
+ name = manifest.name
+ end
+ name = name or path.filename(dir)
+
+ -- the addons installed from a local directory or a git url have no semantic version
+ local version = "latest"
+ local dstdir = _get_addondir(name, version)
+ assert(not os.isdir(dstdir), "addon(%s) already exists!", name)
+
+ -- the addons which it depends on are not installed by the local installation,
+ -- so we need to install them from the repositories first
+ if manifest then
+ for _, dep in ipairs(manifest.deps) do
+ if not addon.addons()[addon.dirname(dep)] then
+ _install_from_repo({dep})
+ end
+ end
+ end
+
+ for _, payloaddir in ipairs(addon.payloads_of(payloadroot)) do
+ os.vcp(path.join(payloadroot, payloaddir), path.join(dstdir, payloaddir))
+ end
+
+ -- we need to roll back the installed payloads if it cannot be registered, e.g. the name conflicts
+ try
+ {
+ function ()
+ addon.register(name, version, manifest and {
+ description = manifest.description,
+ deps = #manifest.deps > 0 and manifest.deps or nil,
+ globalmodules = #manifest.globalmodules > 0 and manifest.globalmodules or nil})
+ end,
+ catch
+ {
+ function (errors)
+ os.tryrm(dstdir)
+ -- we need to remove the addon directory too if no other version is installed
+ local addondir = path.directory(dstdir)
+ if #os.filedirs(path.join(addondir, "*")) == 0 then
+ os.tryrm(addondir)
+ end
+ raise(errors)
+ end
+ }
+ }
+ cprint("${color.success}install ${bright}%s${clear} ok!", name)
+end
+
+-- install a single addon from a git url or github shortcut, e.g. https://github.com/xmake-addons/serial-monitor
+function _install_from_git(url)
+ local branch
+ if url:startswith("github:") then
+ local i = url:find("#", 1, true)
+ if i then
+ branch = url:sub(i + 1)
+ url = url:sub(1, i - 1)
+ end
+ url = git.asgiturl(url)
+ end
+ local name = (path.filename(url):gsub("%.git$", ""))
+ local tmpdir = os.tmpfile() .. ".dir"
+
+ -- we need git to clone it, it will be installed first if it's not found
+ --
+ -- @note we cannot enter it for the other install paths, it may install packages
+ -- and that needs a project, but `xmake addon` can be run anywhere
+ --
+ environment.enter()
+ try
+ {
+ function ()
+ git.clone(url, {verbose = option.get("verbose"), branch = branch, outputdir = tmpdir})
+ os.tryrm(path.join(tmpdir, ".git"))
+ _install_from_local(tmpdir, name)
+ end,
+ finally
+ {
+ -- we always need to remove the temporary clone directory,
+ -- @note try() swallows the errors if we do not re-raise them here
+ function (ok, errors)
+ os.tryrm(tmpdir)
+ if not ok then
+ raise(errors)
+ end
+ end
+ }
+ }
+ environment.leave()
+end
+
+-- does the given name come from a repository? e.g. `myaddon`, `myrepo@myaddon`
+function _is_from_repo(name)
+ -- github shortcut: github:user/repo or github:user/repo#branch
+ if name:startswith("github:") then
+ return false
+ end
+
+ -- local directory
+ --
+ -- @note we need to check it before the git url, `git.asgiturl` also accepts
+ -- the local paths with a trailing separator, e.g. `/path/to/myaddon/`
+ if os.isdir(name) then
+ return false
+ end
+ return not git.asgiturl(name)
+end
+
+-- install addons
+function _install()
+ local names = assert(option.get("addons"), "please specify the addons to be installed!")
+
+ -- the addons from the repositories are installed in one shot, the others one by one
+ local requires = {}
+ for _, name in ipairs(names) do
+ if _is_from_repo(name) then
+ table.insert(requires, name)
+ elseif os.isdir(name) then
+ _install_from_local(name)
+ else
+ _install_from_git(name)
+ end
+ end
+ if #requires > 0 then
+ _install_from_repo(requires)
+ end
+end
+
+-- remove the given installed addons
+--
+-- @note `xrepo remove --addon` removes them in the same way,
+-- @see xmake/modules/private/xrepo/action/remove.lua
+--
+function _remove()
+ local names = option.get("addons")
+ local force = option.get("force")
+
+ -- remove all the installed addons? e.g. xmake addon --remove --all
+ if option.get("all") then
+ names = table.keys(addon.addons())
+ if #names == 0 then
+ wprint("no installed addons!")
+ return
+ end
+ -- the dependencies between them do not matter, they are all removed
+ force = true
+ end
+
+ assert(names, "please specify the addon name to be removed!")
+ for _, name in ipairs(names) do
+ addon.remove(name, {force = force})
+ cprint("${color.success}remove ${bright}%s${clear} ok!", name)
+ end
+end
+
+-- upgrade the addons which the current project declares, e.g. add_addons("esp32-devel 1.0.x")
+function _upgrade()
+ local declarations = {addons = table.wrap(project.get("addons")),
+ repositories = table.wrap(project.get("repositories"))}
+ assert(#declarations.addons > 0, "no addons are declared in this project, e.g. add_addons(\"esp32-devel\")!")
+ install_addons(os.projectdir(), declarations, {upgrade = true})
+end
+
+-- search the addons from the repositories
+function _search()
+ local patterns = assert(option.get("addons"), "please specify the addon name pattern to be searched!")
+ xrepo_addon("search", patterns)
+end
+
+-- collect the installed addons from the addons registry
+function _collect_installed_addons()
+ local entries = {}
+ for name, addoninfo in table.orderpairs(addon.addons()) do
+ -- an addon can be installed with several versions, we show the active one,
+ -- the projects can lock the other ones, @see core/project/addons.lua
+ local versions = addon.versions(name)
+ table.insert(entries, {name = name, version = addoninfo.version,
+ versions = #versions > 1 and versions or nil,
+ description = addoninfo.description, payloads = addoninfo.payloads})
+ end
+ return entries
+end
+
+-- print an addon entry, e.g. -> serial-monitor v1.0.1: monitor the serial port output (in xmake-repo)
+function _print_addon(entry, suffix)
+ local title = entry.name
+ if entry.version then
+ title = title .. " " .. entry.version
+ end
+ local description = entry.description and (": " .. entry.description) or ""
+ cprint(" ${color.dump.reference}->${clear} ${color.dump.string}%s${clear}%s%s", title, description, suffix or "")
+end
+
+-- get the addons in the repositories, we reuse the packages search here
+function _collect_repo_addons(exclude)
+ local entries = {}
+ for _, results in table.orderpairs(search_packages({"*"}, {kind = "addon", description = false})) do
+ for _, result in ipairs(results) do
+ if not exclude[result.name] then
+ table.insert(entries, result)
+ end
+ end
+ end
+ table.sort(entries, function (a, b) return a.name < b.name end)
+ return entries
+end
+
+-- list all addons
+function _list()
+
+ -- show the installed addons
+ local installed = _collect_installed_addons()
+ local exclude = {}
+ cprint("${bright}the installed addons:${clear}")
+ if #installed > 0 then
+ for _, entry in ipairs(installed) do
+ exclude[entry.name] = true
+ local suffix = string.format(" ${dim}(%s)${clear}", table.concat(entry.payloads, ", "))
+ if entry.versions then
+ suffix = suffix .. string.format(" ${dim}[installed: %s]${clear}", table.concat(entry.versions, ", "))
+ end
+ _print_addon(entry, suffix)
+ end
+ else
+ print(" (none)")
+ end
+
+ -- show the addons in the repositories, we only show the first ones if there are too many
+ local listlimit = 10
+ local avail = _collect_repo_addons(exclude)
+ cprint("${bright}the available addons:${clear} ${dim}(run `xmake addon --install <name>` to install," ..
+ " `--search <pattern>` to search)${clear}")
+ if #avail > 0 then
+ for idx, entry in ipairs(avail) do
+ if idx > listlimit then
+ cprint(" ${dim}... and %d more${clear}", #avail - listlimit)
+ break
+ end
+ _print_addon(entry, entry.reponame and string.format(" ${dim}(in %s)${clear}", entry.reponame) or nil)
+ end
+ else
+ print(" (none)")
+ end
+end
+
+function main()
+ if option.get("install") then
+ _install()
+ elseif option.get("remove") then
+ _remove()
+ elseif option.get("upgrade") then
+ _upgrade()
+ elseif option.get("list") then
+ _list()
+ elseif option.get("search") then
+ _search()
+ end
+end
diff --git a/xmake/actions/addon/xmake.lua b/xmake/actions/addon/xmake.lua
new file mode 100644
index 000000000..a0266aa4f
--- /dev/null
+++ b/xmake/actions/addon/xmake.lua
@@ -0,0 +1,49 @@
+--!A cross-platform build utility based on Lua
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+-- Copyright (C) 2015-present, Xmake Open Source Community.
+--
+-- @author ruki
+-- @file xmake.lua
+--
+
+task("addon")
+ set_category("action")
+ on_run("main")
+ set_menu {
+ usage = "xmake addon [options]",
+ description = "Manage addons of xmake.",
+ options = {
+ {'i', "install", "k", nil, "Install addons."},
+ {'r', "remove", "k", nil, "Remove the given installed addons."},
+ {'s', "search", "k", nil, "Search the addons from the repositories."},
+ {'l', "list", "k", nil, "List all installed addons."},
+ {'u', "upgrade", "k", nil, "Upgrade the addons which the current project declares."},
+ {nil, "all", "k", nil, "Remove all installed addons, e.g. xmake addon --remove --all"},
+ {'f', "force", "k", nil, "Force to remove the addons, even if they are depended on by the others."},
+ {nil, "addons", "vs", nil, "The addon paths, urls or names.",
+ "e.g.",
+ " $ xmake addon --install https://github.com/myrepo/serial-monitor",
+ " $ xmake addon --install github:myrepo/serial-monitor",
+ " $ xmake addon --install github:myrepo/serial-monitor#dev",
+ " $ xmake addon --install /tmp/my-addon",
+ " $ xmake addon --install xmake-repo@serial-monitor",
+ " $ xmake addon --install serial-monitor",
+ " $ xmake addon --remove serial-monitor",
+ " $ xmake addon --remove --all",
+ " $ xmake addon --upgrade",
+ " $ xmake addon --search serial",
+ " $ xmake addon --list"}
+ }
+ }
diff --git a/xmake/actions/create/main.lua b/xmake/actions/create/main.lua
index 89a4791a6..f3135fadb 100644
--- a/xmake/actions/create/main.lua
+++ b/xmake/actions/create/main.lua
@@ -25,6 +25,19 @@ import("actions.create.template", {rootdir = os.programdir()})
-- validate template component against path traversal
function _validate_template_component(name, value)
+
+ -- the qualified template id of an addon, e.g. @addon/basic-templates/verilator.console
+ if name == "template id" and value:startswith("@addon/") then
+ local rest = value:sub(#"@addon/" + 1)
+ local pos = rest:find("/", 1, true)
+ if not pos then
+ raise("invalid %s: %s, it should be `@addon/<addon>/<template>`!", name, value)
+ end
+ _validate_template_component("addon name", rest:sub(1, pos - 1))
+ _validate_template_component("template id", rest:sub(pos + 1))
+ return
+ end
+
if #value == 0 or value == "." or value == ".."
or value:find("/", 1, true) or value:find("\\", 1, true)
or value:find(":", 1, true) or value:find("\0", 1, true) then
@@ -79,26 +92,12 @@ function _list_templates(lang_filter)
languages = {lang_filter}
end
- -- map a template directory to its root meta (repo/global/builtin)
- local rootinfo_of = function (dir)
- if not dir then
- return
- end
- dir = path.absolute(dir)
- for _, info in ipairs(rootinfos) do
- local rootdir = path.absolute(info.dir)
- if dir == rootdir or dir:startswith(rootdir .. path.sep()) then
- return info
- end
- end
- end
-
local sourcekey_of = function (info)
if not info then
return "unknown"
end
- if info.kind == "repo" then
- return "repo:" .. info.name
+ if info.kind == "addon" then
+ return info.kind .. ":" .. info.name
end
return info.kind or "unknown"
end
@@ -109,7 +108,7 @@ function _list_templates(lang_filter)
if templates and #templates > 0 then
for _, name in ipairs(templates) do
local sourcedir = template.templatedir(lang, name)
- local info = rootinfo_of(sourcedir)
+ local info = template.rootinfo_of(sourcedir)
local key = sourcekey_of(info)
local group = groups[key]
if not group then
@@ -140,9 +139,8 @@ function _list_templates(lang_filter)
local group = groups[key]
if group then
local info = group.info
- if info and info.kind == "repo" then
- local branch = info.branch and (" " .. info.branch) or ""
- cprint("${bright}%s${reset}: %s%s", info.name, info.url or "", branch)
+ if info and info.kind == "addon" then
+ cprint("${bright}%s${reset}: addon %s", info.name, info.version or "")
else
cprint("${bright}%s${reset}", (info and info.kind) or "unknown")
end
@@ -195,7 +193,7 @@ function _create_project(lang, templateid, targetname)
-- create project
local sourcedir = template.templatedir(lang, templateid)
if not sourcedir then
- raise("template(%s/%s): not found!\nyou can try:\n - xrepo update-repo (update repositories)\n - xmake create --list (show available templates)", lang, templateid)
+ raise("template(%s/%s): not found!\nyou can try:\n - xmake addon --search templates (search the addons which provide templates)\n - xmake create --list (show available templates)", lang, templateid)
end
-- get the builtin variables
diff --git a/xmake/actions/create/template.lua b/xmake/actions/create/template.lua
index 1279c1d35..ac39e2ff8 100644
--- a/xmake/actions/create/template.lua
+++ b/xmake/actions/create/template.lua
@@ -22,7 +22,7 @@
import("core.base.global")
import("core.base.hashset")
import("core.language.language")
-import("core.package.repository")
+import("core.package.addon")
-- some builtin template variables in xmake.lua
function builtinvars(targetname)
@@ -33,19 +33,16 @@ end
-- get all template roots with extra meta information
--
-- priority:
--- 1. repo: <global-repo>/templates (including builtin repo templates)
+-- 1. addon: <globaldir>/addons/<name>/<version>/templates
-- 2. global: <globaldir>/templates
+-- 3. builtin: <programdir>/templates
function rootinfos()
local results = {}
- -- get template directories from global repositories
- local repos = repository.repositories({global = true, network = false})
- if repos then
- for _, repo in ipairs(repos) do
- local templatesdir = path.join(repo:directory(), "templates")
- if os.isdir(templatesdir) then
- table.insert(results, {kind = "repo", name = repo:name(), url = repo:url(), branch = repo:branch(), dir = templatesdir})
- end
+ -- get template directories from the installed addons
+ for _, addoninfo in ipairs(addon.payloadinfos("templates")) do
+ if os.isdir(addoninfo.dir) then
+ table.insert(results, {kind = "addon", name = addoninfo.name, version = addoninfo.version, dir = addoninfo.dir})
end
end
@@ -54,9 +51,31 @@ function rootinfos()
if os.isdir(dir) then
table.insert(results, {kind = "global", dir = dir})
end
+ dir = path.join(os.programdir(), "templates")
+ if os.isdir(dir) then
+ table.insert(results, {kind = "builtin", dir = dir})
+ end
return results
end
+-- get the root information of the given template directory
+--
+-- @param templatedir the template directory, e.g. <programdir>/templates/c/console
+-- @return the root information, @see rootinfos
+--
+function rootinfo_of(templatedir)
+ if not templatedir then
+ return
+ end
+ templatedir = path.absolute(templatedir)
+ for _, info in ipairs(rootinfos()) do
+ local rootdir = path.absolute(info.dir)
+ if templatedir == rootdir or templatedir:startswith(rootdir .. path.sep()) then
+ return info
+ end
+ end
+end
+
-- get template root directories
function rootdirs()
local results = {}
@@ -83,6 +102,23 @@ end
function templatedir(lang, templateid)
assert(lang)
assert(templateid)
+
+ -- the qualified template of an addon, e.g. @addon/basic-templates/verilator.console
+ --
+ -- @note the template ids are not namespaced, we only need it to disambiguate the conflicts
+ --
+ if templateid:startswith("@addon/") then
+ local referenceinfo = addon.resolve_reference(templateid, "/", "templates")
+ local subdir = _templateid_subdir(referenceinfo.name)
+ if subdir then
+ local dir = path.join(referenceinfo.dir, lang, subdir)
+ if os.isfile(path.join(dir, "xmake.lua")) then
+ return dir
+ end
+ end
+ return
+ end
+
for _, rootdir in ipairs(rootdirs()) do
local subdir = _templateid_subdir(templateid)
if subdir then
@@ -154,11 +190,21 @@ function languages()
return results
end
+-- get the reference of the given template, e.g. @addon/basic-templates/verilator.console
+function _template_reference(rootinfo, templateid)
+ if rootinfo.kind == "addon" then
+ return string.format("@addon/%s/%s", rootinfo.name, templateid)
+ end
+ return path.join(rootinfo.dir, templateid)
+end
+
-- get all templates for the given language
function templates(lang)
assert(lang)
local found = hashset.new()
- for _, rootdir in ipairs(rootdirs()) do
+ local providers = {}
+ for _, rootinfo in ipairs(rootinfos()) do
+ local rootdir = rootinfo.dir
local templateroot = path.join(rootdir, lang)
local configfiles = os.files(path.join(templateroot, "**", "xmake.lua"))
if configfiles then
@@ -182,7 +228,17 @@ function templates(lang)
end
if ok then
table.insert(accepted, item.dir)
- found:insert((item.relpath:gsub("[/\\]", ".")))
+ local templateid = (item.relpath:gsub("[/\\]", "."))
+ -- the templates are not namespaced, so we need to report the conflicts of the addons,
+ -- otherwise we do not know which template will be used
+ local provider = providers[templateid]
+ if provider and (provider.kind == "addon" or rootinfo.kind == "addon") then
+ wprint("template(%s/%s) conflicts, we will use the first one!\n -> %s\n -> %s\nplease use the qualified template id to disambiguate them.",
+ lang, templateid, _template_reference(provider, templateid), _template_reference(rootinfo, templateid))
+ else
+ providers[templateid] = rootinfo
+ found:insert(templateid)
+ end
end
end
end
diff --git a/xmake/actions/require/xmake.lua b/xmake/actions/require/xmake.lua
index bf81e3810..b800c4528 100644
--- a/xmake/actions/require/xmake.lua
+++ b/xmake/actions/require/xmake.lua
@@ -39,6 +39,9 @@ task("require")
{nil, "linkjobs", "kv", nil, "Set the number of parallel link jobs."},
{nil, "shallow", "k", nil, "Does not install or download dependent packages."},
{nil, "build", "k", nil, "Always build and install packages from source."},
+ {nil, "addon", "k", nil, "Install addon packages from <repository>/addons/."},
+ {nil, "plugin", "k", nil, "Install plugin packages from <repository>/plugins/.",
+ "(deprecated, please use --addon instead)"},
{'l', "list", "k", nil, "List all package dependencies in project.",
"e.g.",
" $ xmake require --list"},
diff --git a/xmake/core/base/binutils.lua b/xmake/core/base/binutils.lua
index 7adfe6efa..400d48332 100644
--- a/xmake/core/base/binutils.lua
+++ b/xmake/core/base/binutils.lua
@@ -64,6 +64,8 @@ end
-- - target_minver: the target minimum version (only for macho)
-- - xcode_sdkver: the Xcode SDK version (only for macho)
-- - zeroend: append null terminator (default: false)
+-- - refobj: a reference elf object whose class/endianness/machine/e_flags
+-- are mirrored into the output (default: derived from arch)
function binutils.bin2obj(binaryfile, outputfile, opt)
opt = opt or {}
local format = opt.format
@@ -94,7 +96,7 @@ function binutils.bin2obj(binaryfile, outputfile, opt)
if not binutils._bin2elf then
return nil, "binutils._bin2elf not available (C implementation not compiled)"
end
- return binutils._bin2elf(binaryfile, outputfile, opt.symbol_prefix or "_binary_", opt.arch or "x86_64", opt.basename, opt.zeroend or false)
+ return binutils._bin2elf(binaryfile, outputfile, opt.symbol_prefix or "_binary_", opt.arch or "x86_64", opt.basename, opt.zeroend or false, opt.refobj)
else
return nil, string.format("unsupported format '%s' (supported: coff, elf, macho)", format)
end
diff --git a/xmake/core/base/interpreter.lua b/xmake/core/base/interpreter.lua
index 83bbdf593..8dc0e4880 100644
--- a/xmake/core/base/interpreter.lua
+++ b/xmake/core/base/interpreter.lua
@@ -879,6 +879,33 @@ function interpreter:scriptdir()
return path.directory(self._PRIVATE._CURFILE)
end
+-- add a resolver for the references of includes(), e.g. includes("@addon/esp32/check")
+--
+-- @param resolver function (interp, reference), it returns the files, or nil and errors
+--
+-- @note the interpreter knows nothing about the references, the callers register the
+-- resolvers which they support, e.g. @see project._interpreter()
+--
+function interpreter:includes_resolver_add(resolver)
+ local resolvers = self._PRIVATE._INCLUDES_RESOLVERS or {}
+ table.insert(resolvers, resolver)
+ self._PRIVATE._INCLUDES_RESOLVERS = resolvers
+end
+
+-- do we ignore the unresolvable references of includes()? e.g. includes("@addon/esp32/board")
+function interpreter:includes_unresolved()
+ return self._PRIVATE._INCLUDES_UNRESOLVED
+end
+
+-- ignore the unresolvable references of includes() instead of raising errors
+--
+-- @note the project file may reference the resources which have not been installed yet,
+-- so the caller can load it, install them and load it again, @see project._load()
+--
+function interpreter:includes_unresolved_set(enabled)
+ self._PRIVATE._INCLUDES_UNRESOLVED = enabled
+end
+
-- set root scope kind
--
-- the root api will affect these scopes
@@ -1786,6 +1813,15 @@ function interpreter:api_builtin_set_xmakever(minver)
end
-- the builtin api: includes()
+-- find the include files of the builtin includes, e.g. includes("@builtin/check")
+function interpreter:_find_builtin_includes(subpath)
+ local builtin_path = subpath:sub(#"@builtin/" + 1)
+ if builtin_path:endswith(".lua") then
+ return os.files(path.join(os.programdir(), "includes", builtin_path))
+ end
+ return os.files(path.join(os.programdir(), "includes", builtin_path, "xmake.lua"))
+end
+
function interpreter:api_builtin_includes(...)
assert(self and self._PRIVATE and self._PRIVATE._ROOTDIR and self._PRIVATE._MTIMES)
local curfile = self._PRIVATE._CURFILE
@@ -1799,18 +1835,31 @@ function interpreter:api_builtin_includes(...)
-- attempt to find files from programdir/includes/*.lua
-- e.g. includes("@builtin/check")
if subpath:startswith("@builtin/") then
- local builtin_path = subpath:sub(10)
- local files
- if builtin_path:endswith(".lua") then
- files = os.files(path.join(os.programdir(), "includes", builtin_path))
- else
- files = os.files(path.join(os.programdir(), "includes", builtin_path, "xmake.lua"))
- end
+ local files = self:_find_builtin_includes(subpath)
if files and #files > 0 then
table.join2(subpaths_matched, files)
found = true
end
end
+ -- attempt to find files from the registered resolvers of the references
+ -- e.g. includes("@addon/esp32/check"), @see interpreter:includes_resolver_add()
+ if not found and subpath:startswith("@") then
+ for _, resolver in ipairs(self._PRIVATE._INCLUDES_RESOLVERS or {}) do
+ local files, errors = resolver(self, subpath)
+ if files then
+ table.join2(subpaths_matched, files)
+ found = true
+ break
+ elseif errors then
+ -- it has not been resolved yet? the caller may load this file again
+ if self:includes_unresolved() then
+ found = true
+ break
+ end
+ os.raise(errors)
+ end
+ end
+ end
-- find the given files from the project directory
if not found then
local files
diff --git a/xmake/core/base/os.lua b/xmake/core/base/os.lua
index 005f18a70..89be6d638 100644
--- a/xmake/core/base/os.lua
+++ b/xmake/core/base/os.lua
@@ -423,8 +423,14 @@ function os.match(pattern, mode, opt)
return os._async_task().match(pattern, mode)
end
- -- extract callback
- local callback = type(opt) == "function" and opt or (type(opt) == "table" and opt.callback or nil)
+ -- extract callback and the maximum recursion level
+ local callback, maxrecursion
+ if type(opt) == "function" then
+ callback = opt
+ elseif type(opt) == "table" then
+ callback = opt.callback
+ maxrecursion = opt.recursion
+ end
-- support path instance
pattern = tostring(pattern)
@@ -493,7 +499,9 @@ function os.match(pattern, mode, opt)
-- limit recursion level: src/*/*.c
local recursion = 0
if pattern:find("**", 1, true) then
- recursion = -1
+ -- we can also limit the recursion level of `**`, it may be very slow
+ -- in a deep directory tree, e.g. os.files("src/**.c", {recursion = 2})
+ recursion = maxrecursion or -1
else
-- "src/*/*.c" -> "*/" -> recursion level: 1
-- "src/*/main.c" -> "*/" -> recursion level: 1
@@ -504,6 +512,9 @@ function os.match(pattern, mode, opt)
recursion = seps
end
end
+ if maxrecursion and recursion > maxrecursion then
+ recursion = maxrecursion
+ end
end
-- convert pattern to a lua pattern
diff --git a/xmake/core/base/poller.lua b/xmake/core/base/poller.lua
index bb18dfda4..43c3bea1a 100644
--- a/xmake/core/base/poller.lua
+++ b/xmake/core/base/poller.lua
@@ -124,7 +124,7 @@ function poller:remove(obj)
end
-- remove poller object data
- self:_pollerdata_set(obj, nil)
+ self:_pollerdata_set(obj:cdata(), nil)
return true
end
@@ -153,13 +153,15 @@ function poller:wait(timeout)
local otype = v[1]
local cdata = v[2]
local events = v[3]
- local pollerdata = self:_pollerdata(cdata)
- if not pollerdata then
- return -1, string.format("no object data for cdata(%s)!", cdata)
+ -- this object may have been removed from the poller while its event
+ -- was already collected, e.g. a pending overlapped io on windows,
+ -- we just drop it, it has no owner any more, @see poller:remove()
+ local pollerdata = self:_pollerdata(cdata)
+ if pollerdata then
+ local obj = pollerdata[1]
+ assert(obj and obj:otype() == otype and obj:cdata() == cdata)
+ table.insert(results, {obj, events, pollerdata[2]})
end
- local obj = pollerdata[1]
- assert(obj and obj:otype() == otype and obj:cdata() == cdata)
- table.insert(results, {obj, events, pollerdata[2]})
end
end
return count, results
diff --git a/xmake/core/base/scheduler.lua b/xmake/core/base/scheduler.lua
index a59c38207..4ae8d74e0 100644
--- a/xmake/core/base/scheduler.lua
+++ b/xmake/core/base/scheduler.lua
@@ -304,9 +304,17 @@ end
function scheduler:_poller_events_cb(obj, events)
-- get poller object data
+ --
+ -- the object may have been cancelled while its event was already queued,
+ -- e.g. a process which exits right after we stopped waiting for it,
+ -- @see scheduler:poller_cancel()
+ --
+ -- such an event has no owner any more, we just drop it: it is not an
+ -- error of the scheduler and it must not abort the whole loop
local pollerdata = self:_poller_data(obj)
if not pollerdata then
- return false, string.format("%s: cannot get poller data!", obj)
+ utils.dprint("%s: drop the event(%d), it has been cancelled!", obj, events)
+ return true
end
-- is process/fwatcher object?
@@ -1068,6 +1076,10 @@ function scheduler:poller_waitproc(obj, timeout)
running:waitobj_set(obj)
-- wait
+ --
+ -- @note we keep this process in the poller if it is timeout, so its exit status
+ -- is still saved as a pending status when it exits later, and the next wait
+ -- returns it immediately, @see scheduler:_poller_events_cb()
local ok = self:co_suspend()
return ok, pollerdata.object_event
end
diff --git a/xmake/core/base/semver.lua b/xmake/core/base/semver.lua
index 7c2b6ea8d..fc989e18d 100644
--- a/xmake/core/base/semver.lua
+++ b/xmake/core/base/semver.lua
@@ -232,5 +232,15 @@ function semver.match(str, pos, pattern)
end
end
+-- is a valid semantic version? e.g. "1.2.3", "v1.2.3-beta"
+function semver.is_valid(version)
+ return semver.parse(version) ~= nil
+end
+
+-- is a valid semantic version range? e.g. ">=1.0 <2.0", "^1.2", "master || >1.4"
+function semver.is_valid_range(range)
+ return semver.satisfies("1.0", range) ~= nil
+end
+
-- return module: semver
return semver
diff --git a/xmake/core/base/task.lua b/xmake/core/base/task.lua
index 27ad5023a..2b3788603 100644
--- a/xmake/core/base/task.lua
+++ b/xmake/core/base/task.lua
@@ -24,10 +24,12 @@ local task = task or {}
-- load modules
local os = require("base/os")
local table = require("base/table")
+local utils = require("base/utils")
local string = require("base/string")
local global = require("base/global")
local hashset = require("base/hashset")
local interpreter = require("base/interpreter")
+local addon = require("package/addon")
local sandbox = require("sandbox/sandbox")
local config = require("project/config")
local sandbox_os = require("sandbox/modules/os")
@@ -81,9 +83,23 @@ end
-- the directories of tasks
function task._directories()
- return {path.join(global.directory(), "plugins"),
- path.join(os.programdir(), "plugins"),
- path.join(os.programdir(), "actions")}
+ local dirs = task._DIRECTORIES
+ if dirs == nil then
+ -- add the plugins of the installed addons first, e.g. ~/.xmake/addons/<name>/<version>/plugins
+ --
+ -- we get them from the addons registry file directly,
+ -- so we do not need to scan the whole addons directory on startup
+ --
+ -- @note the first one wins, so an addon is able to take over a deprecated
+ -- builtin plugin, e.g. `xmake format`
+ --
+ dirs = addon.payloads("plugins")
+ table.insert(dirs, path.join(global.directory(), "plugins"))
+ table.insert(dirs, path.join(os.programdir(), "plugins"))
+ table.insert(dirs, path.join(os.programdir(), "actions"))
+ task._DIRECTORIES = dirs
+ end
+ return dirs
end
-- translate menu
@@ -96,7 +112,7 @@ function task._translate_menu(taskname, menu)
local options_full = {}
for _, opt in ipairs(options) do
if type(opt) == "function" then
- local ok, results = sandbox.load(opt)
+ local ok, results = sandbox.call(opt)
if ok then
if results then
for _, opt in ipairs(results) do
@@ -141,7 +157,7 @@ function task._translate_menu(taskname, menu)
opt[i] = function ()
-- call it in the sandbox
- local ok, results = sandbox.load(description)
+ local ok, results = sandbox.call(description)
if not ok then
return nil, string.format("taskmenu: %s", results)
end
@@ -385,6 +401,39 @@ function task.new(name, info)
return instance
end
+-- is the given plugin conflicting with the loaded one?
+--
+-- the plugins are not namespaced, so the first one always wins, @see task._directories(),
+-- but we need to report the conflicts of the addons, otherwise we do not know which
+-- plugin will be run
+--
+-- @param taskname the task name
+-- @param taskfile the task file of the loaded plugin, it will be nil if it's the first one
+-- @param filepath the task file of the plugin which we are loading
+--
+function task._is_conflicting(taskname, taskfile, filepath)
+ if not taskfile then
+ return false
+ end
+
+ -- we only report it if both of them come from the addons, taking over a builtin
+ -- plugin is expected, e.g. `xmake format` has been moved to an addon
+ --
+ -- @note we cannot raise errors here, otherwise all the commands will be broken,
+ -- and the user cannot even remove the conflicting addons
+ local addondir = path.absolute(addon.installdir())
+ if path.absolute(taskfile):startswith(addondir) and path.absolute(filepath):startswith(addondir) then
+ utils.warning("plugin(%s) conflicts, we will use the first one!\n -> %s\n -> %s", taskname, taskfile, filepath)
+ end
+ return true
+end
+
+-- clear the loaded tasks, e.g. some addons may be installed just now
+function task.clear()
+ task._TASKS = nil
+ task._DIRECTORIES = nil
+end
+
-- get all registered tasks
--
-- @return the tasks table {name = task, ...}
@@ -396,6 +445,7 @@ function task.tasks()
-- load tasks
local tasks = {}
+ local taskfiles = {}
local dirs = task._directories()
for _, dir in ipairs(dirs) do
local files = os.files(path.join(dir, "*", "xmake.lua"))
@@ -403,7 +453,12 @@ function task.tasks()
for _, filepath in ipairs(files) do
local results, errors = task._load(filepath)
if results then
- table.join2(tasks, results)
+ for taskname, taskinfo in pairs(results) do
+ if not task._is_conflicting(taskname, taskfiles[taskname], filepath) then
+ taskfiles[taskname] = filepath
+ tasks[taskname] = taskinfo
+ end
+ end
else
os.raise(errors)
end
@@ -508,7 +563,7 @@ function task:run(...)
local curdir = os.curdir()
-- run task
- local ok, errors = sandbox.load(on_run, ...)
+ local ok, errors = sandbox.call(on_run, ...)
-- restore the current directory
os.cd(curdir)
diff --git a/xmake/core/base/thread.lua b/xmake/core/base/thread.lua
index 187f98d53..6c3fc5169 100644
--- a/xmake/core/base/thread.lua
+++ b/xmake/core/base/thread.lua
@@ -942,7 +942,7 @@ function thread._run_thread(callback_str, callinfo_str)
end
-- do callback
- local ok, errors = sandbox.load(sandbox_inst:script(), table.unpack(argv or {}))
+ local ok, errors = sandbox.call(sandbox_inst:script(), table.unpack(argv or {}))
-- thread is finished, we need to notify the waited thread
if wpipe then
diff --git a/xmake/core/language/language.lua b/xmake/core/language/language.lua
index 8c6846819..1ea960807 100644
--- a/xmake/core/language/language.lua
+++ b/xmake/core/language/language.lua
@@ -50,7 +50,7 @@ function _instance:get(name)
end
if self._g == nil and info.load ~= nil then
- local ok, results = sandbox.load(info.load)
+ local ok, results = sandbox.call(info.load)
if not ok then
os.raise(results)
end
diff --git a/xmake/core/package/addon.lua b/xmake/core/package/addon.lua
new file mode 100644
index 000000000..d6798f56f
--- /dev/null
+++ b/xmake/core/package/addon.lua
@@ -0,0 +1,826 @@
+--!A cross-platform build utility based on Lua
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+-- Copyright (C) 2015-present, Xmake Open Source Community.
+--
+-- @author ruki
+-- @file addon.lua
+--
+
+-- define module
+local addon = addon or {}
+
+-- load modules
+local os = require("base/os")
+local io = require("base/io")
+local path = require("base/path")
+local table = require("base/table")
+local utils = require("base/utils")
+local global = require("base/global")
+
+-- the payload directories of an addon
+--
+-- an addon can provide any subset of them, e.g. only `plugins`
+--
+-- @note only `plugins` is activated for now, the others are reserved
+--
+function addon._payloaddirs()
+ return {"plugins", "rules", "toolchains", "platforms", "modules", "templates", "themes", "includes"}
+end
+
+-- the manifest file of an addon, e.g. <sourcedir>/addon.lua
+--
+-- an addon describes itself in this file, so its name and layout never depend on
+-- the package name of the repository which distributes it
+--
+function addon._manifestfile(sourcedir)
+ return path.join(sourcedir, "addon.lua")
+end
+
+-- the interpreter of the addon manifest
+function addon._interpreter()
+ local interp = addon._INTERPRETER
+ if interp == nil then
+ -- we need to load it lazily, the interpreter also depends on this module
+ local interpreter = require("base/interpreter")
+ interp = interpreter.new()
+ interp:api_define(addon.apis())
+ addon._INTERPRETER = interp
+ end
+ return interp
+end
+
+-- the registry file of the installed addons, e.g. ~/.xmake/addons/addons.conf
+--
+-- we save all installed addons to this file when installing/removing them,
+-- so we do not need to scan the whole addons directory on startup
+--
+function addon._registryfile()
+ return path.join(addon.installdir(), "addons.conf")
+end
+
+-- save the given registry to the registry file
+function addon._save(registry)
+ addon._REGISTRY = registry
+ addon._ADDONS = nil
+ local registryfile = addon._registryfile()
+ -- we need not create an empty registry file if no addons are installed
+ if table.empty(registry) and not os.isfile(registryfile) then
+ return
+ end
+ local ok, errors = io.save(registryfile, registry)
+ if not ok then
+ utils.warning(errors)
+ end
+end
+
+-- get the payload directory of the given addon
+--
+-- @param name the addon name, e.g. "esp32"
+-- @param kind the payload kind, e.g. "rules", "modules"
+-- @return the directory, e.g. ~/.xmake/addons/esp32/v1.0.0/rules
+--
+function addon._payloaddir(name, kind)
+ local dirname = addon.dirname(name)
+ local addoninfo = addon.addons()[dirname]
+ if addoninfo and table.contains(addoninfo.payloads or {}, kind) then
+ return path.join(addon.installdir(), dirname, addoninfo.version, kind)
+ end
+end
+
+-- get the plugin task names of the given addon directory
+--
+-- the plugins are not namespaced, we need them to check the conflicts
+--
+function addon._plugins_of(addondir)
+ local plugins = {}
+ for _, filepath in ipairs(os.files(path.join(addondir, "plugins", "*", "xmake.lua"))) do
+ local content = io.readfile(filepath)
+ if content then
+ for taskname in content:gmatch("task%s*%(%s*\"(.-)\"") do
+ table.insert(plugins, taskname)
+ end
+ end
+ end
+ return plugins
+end
+
+-- get the template ids of the given addon directory, e.g. {"c/console"}
+--
+-- the templates are not namespaced, we need them to check the conflicts
+--
+function addon._templates_of(addondir)
+ local templates = {}
+ local templatesdir = path.join(addondir, "templates")
+ for _, langdir in ipairs(os.dirs(path.join(templatesdir, "*"))) do
+ local lang = path.filename(langdir)
+ local accepted = {}
+ for _, filepath in ipairs(os.files(path.join(langdir, "**", "xmake.lua"))) do
+ local dir = path.directory(filepath)
+ local relpath = path.relative(dir, langdir)
+ if relpath and relpath ~= "." then
+ local nested = false
+ for _, root in ipairs(accepted) do
+ if dir:startswith(root .. path.sep()) then
+ nested = true
+ break
+ end
+ end
+ if not nested then
+ table.insert(accepted, dir)
+ table.insert(templates, lang .. "/" .. (relpath:gsub("[/\\]", ".")))
+ end
+ end
+ end
+ end
+ return templates
+end
+
+-- check the conflicts of the plugins and templates, they are not namespaced
+--
+-- @param dirname the addon directory name
+-- @param addoninfo the addon information, @see addon.register
+--
+-- @return the errors if there are some conflicts
+--
+function addon._check_conflicts(dirname, addoninfo)
+ local kindnames = {plugins = "plugin", templates = "template", globalmodules = "global module"}
+ for _, kind in ipairs({"plugins", "templates", "globalmodules"}) do
+ for _, name in ipairs(addoninfo[kind] or {}) do
+ for otherdirname, otheraddoninfo in pairs(addon.addons()) do
+ if otherdirname ~= dirname and table.contains(otheraddoninfo[kind] or {}, name) then
+ return string.format("%s(%s) conflicts, it has been provided by the addon(%s)!\nplease remove one of them, e.g. xmake addon --remove %s",
+ kindnames[kind], name, otherdirname, otherdirname)
+ end
+ end
+ end
+ end
+
+ -- the global modules can also conflict with the builtin and the user modules
+ --
+ -- @note the `core.*` modules are in the core directory of the sandbox,
+ -- they are not in `<programdir>/modules`,
+ -- @see core/sandbox/modules/import/core/sandbox/module.lua
+ local moduledirs = {path.join(os.programdir(), "modules"),
+ path.join(os.programdir(), "core", "sandbox", "modules", "import"),
+ path.join(global.directory(), "modules")}
+ for _, name in ipairs(addoninfo.globalmodules or {}) do
+ local modulepath = (name:gsub("%.", "/")) .. ".lua"
+ for _, moduledir in ipairs(moduledirs) do
+ if os.isfile(path.join(moduledir, modulepath)) then
+ return string.format("global module(%s) conflicts, it has been provided by %s!\nplease rename it in the addon manifest.",
+ name, moduledir:startswith(os.programdir()) and "xmake" or moduledir)
+ end
+ end
+ end
+end
+
+-- get the addons which depend on the given addon
+function addon._parents(name)
+ local dirname = addon.dirname(name)
+ local parents
+ for otherdirname, entry in table.orderpairs(addon._registry()) do
+ if otherdirname ~= dirname then
+ for _, addoninfo in pairs(entry.versions or {}) do
+ if table.contains(addoninfo.deps or {}, dirname) then
+ parents = parents or {}
+ table.insert(parents, otherdirname)
+ break
+ end
+ end
+ end
+ end
+ return parents
+end
+
+-- unregister the given addon or only one of its versions
+function addon.unregister(name, version)
+ local dirname = addon.dirname(name)
+ local registry = addon._registry()
+ local entry = registry[dirname]
+ if entry == nil then
+ return
+ end
+ if version then
+ entry.versions[version] = nil
+ if entry.active == version then
+ -- we need to select the other one deterministically
+ entry.active = addon.versions(name)[1]
+ end
+ if table.empty(entry.versions) then
+ registry[dirname] = nil
+ end
+ else
+ registry[dirname] = nil
+ end
+ addon._save(registry)
+end
+
+-- get the apis of the addon manifest
+function addon.apis()
+ return {
+ values = {
+ -- addon.set_xxx
+ "addon.set_description"
+ , "addon.set_homepage"
+ , "addon.set_license"
+ , "addon.set_sourcedir"
+ -- addon.add_xxx
+ , "addon.add_deps"
+ , "addon.add_globalmodules"
+ }
+ }
+end
+
+-- get the manifest of the given addon directory
+--
+-- @param sourcedir the addon source or install directory, which contains `addon.lua`
+--
+-- @return the manifest, e.g. {name = "esp32-devel", description = "...", sourcedir = "src", deps = {"serial-tools"}}
+-- it will be nil if this addon does not describe itself
+--
+function addon.manifest(sourcedir)
+
+ -- we may resolve a lot of `@self` references, so we need to cache them
+ local manifests = addon._MANIFESTS
+ if manifests == nil then
+ manifests = {}
+ addon._MANIFESTS = manifests
+ end
+ local cachekey = path.absolute(sourcedir)
+ local cacheinfo = manifests[cachekey]
+ if cacheinfo ~= nil then
+ return cacheinfo or nil
+ end
+
+ local manifestfile = addon._manifestfile(sourcedir)
+ if not os.isfile(manifestfile) then
+ manifests[cachekey] = false
+ return
+ end
+ local interp = addon._interpreter()
+ local ok, errors = interp:load(manifestfile)
+ if not ok then
+ return nil, errors
+ end
+ local results, errors = interp:make("addon", true, true)
+ if not results then
+ return nil, errors
+ end
+ local manifest
+ for name, addoninfo in pairs(results) do
+ if manifest then
+ return nil, string.format("%s: only one addon() scope is allowed!", manifestfile)
+ end
+ manifest = {name = name,
+ description = addoninfo:get("description"),
+ homepage = addoninfo:get("homepage"),
+ license = addoninfo:get("license"),
+ sourcedir = addoninfo:get("sourcedir"),
+ deps = table.wrap(addoninfo:get("deps")),
+ globalmodules = table.wrap(addoninfo:get("globalmodules"))}
+ end
+ if not manifest then
+ return nil, string.format("%s: no addon() scope found!", manifestfile)
+ end
+ manifests[cachekey] = manifest
+ return manifest
+end
+
+-- get a working directory which has no project
+--
+-- we need it to run the sub-processes of the addons, e.g. `xrepo install --addon`,
+-- otherwise they would load the project of the current directory again
+--
+-- @note we cannot use `os.tmpdir()` directly, it is shared by all the commands,
+-- e.g. a stray `xmake.lua` in it would break the isolation
+--
+-- @note we can share it between the processes, we only use it as the working directory
+-- and never write anything into it, @see private/action/addon/impl/xrepo.lua
+--
+function addon.workdir()
+ local workdir = path.join(os.tmpdir(), "addons", "working")
+ if not os.isdir(workdir) then
+ -- it may be created by the other processes at the same time, we can ignore it
+ os.mkdir(workdir)
+ end
+ return workdir
+end
+
+-- the install directory of addons, e.g. ~/.xmake/addons
+function addon.installdir()
+ return path.join(global.directory(), "addons")
+end
+
+-- get the directory name of the given addon name, e.g. "myns::foo" -> "myns_foo"
+function addon.dirname(name)
+ return (name:lower():gsub("::", "_"))
+end
+
+-- is the given reference an addon reference?
+--
+-- e.g. "@addon/esp32/flash", "@self/flash", "@addon.esp32.sdkconfig", "@self.sdkconfig"
+--
+function addon.is_reference(reference, sep)
+ return reference:startswith("@addon" .. sep) or reference:startswith("@self" .. sep)
+end
+
+-- get the addon which owns the given script directory
+--
+-- it's used to resolve the `@self` references inside an addon,
+-- so that the addon code never needs to know its own installed name
+--
+-- @param scriptdir the script directory, e.g. ~/.xmake/addons/esp32/v1.0.0/rules/flash
+-- it will be the directory of the caller script by default
+-- @return the addon name and its root directory, e.g. esp32, ~/.xmake/addons/esp32/v1.0.0
+--
+function addon.owner(scriptdir)
+ if not scriptdir then
+ -- we can get it from the sandbox of the caller script, e.g. the rule script of an addon
+ local sandbox = require("sandbox/sandbox")
+ local instance = sandbox.instance()
+ scriptdir = instance and instance:rootdir()
+ end
+ if not scriptdir then
+ return
+ end
+ scriptdir = path.absolute(scriptdir)
+
+ -- the installed addons, e.g. ~/.xmake/addons/<name>/<version>/...
+ local installdir = path.absolute(addon.installdir())
+ if scriptdir:startswith(installdir .. path.sep()) then
+ local parts = path.split(path.relative(scriptdir, installdir))
+ if #parts >= 2 then
+ local addondir = path.join(installdir, parts[1], parts[2])
+ -- the registry keeps the raw addon name, the directory name is only
+ -- its normalized form, e.g. "myns::foo" -> "myns_foo"
+ local addoninfo = addon.addons()[parts[1]]
+ return addoninfo and addoninfo.name or parts[1], addondir
+ end
+ return
+ end
+
+ -- the addon source directory, we can also run the addon code in place when developing it
+ local dir = scriptdir
+ while dir and #dir > 0 do
+ -- the addon describes itself? we get its name from the manifest
+ local manifest = addon.manifest(dir)
+ if manifest then
+ return manifest.name, dir
+ end
+ -- otherwise we can only guess it from the payload directories
+ for _, payloaddir in ipairs(addon._payloaddirs()) do
+ if os.isdir(path.join(dir, payloaddir)) then
+ return path.filename(dir), dir
+ end
+ end
+ local parentdir = path.directory(dir)
+ if not parentdir or parentdir == dir then
+ break
+ end
+ dir = parentdir
+ end
+end
+
+-- resolve the given addon reference to its payload directory
+--
+-- the addon resources are referenced with the addon name from the outside,
+-- and with `@self` from the addon code itself, e.g.
+--
+-- add_rules("@addon/esp32/flash"), import("@addon.esp32.sdkconfig") -- from a project
+-- add_rules("@self/flash"), import("@self.sdkconfig") -- from the addon itself
+--
+-- @param reference the reference, e.g. "@addon/esp32/flash", "@self.sdkconfig"
+-- @param sep the separator, e.g. "/", "."
+-- @param kind the payload kind, e.g. "rules", "modules"
+-- @param opt the options, e.g. {scriptdir = "..."}, it's used to resolve `@self`
+--
+-- @return the reference information and errors,
+-- e.g. {dir = "~/.xmake/addons/esp32/v1.0.0/rules", name = "flash", addon = "esp32"}
+--
+function addon.resolve_reference(reference, sep, kind, opt)
+ opt = opt or {}
+
+ -- resolve the `@self` reference from the addon which owns the current script
+ if reference:startswith("@self" .. sep) then
+ local name = reference:sub(#("@self" .. sep) + 1)
+ if name == "" then
+ return nil, string.format("invalid addon reference(%s)!", reference)
+ end
+ local addonname, addondir = addon.owner(opt.scriptdir)
+ if not addondir then
+ return nil, string.format("%s: cannot resolve `@self`, it can only be used inside an addon!", reference)
+ end
+ return {dir = path.join(addondir, kind), name = name, addon = addonname}
+ end
+
+ -- resolve the `@addon` reference, the addon name is always required
+ local prefix = "@addon" .. sep
+ if not reference:startswith(prefix) then
+ return
+ end
+ local pos = reference:find(sep, #prefix + 1, true)
+ local addonname = pos and reference:sub(#prefix + 1, pos - 1)
+ local name = pos and reference:sub(pos + 1)
+ if not addonname or addonname == "" or not name or name == "" then
+ return nil, string.format("invalid addon reference(%s), it should be `@addon%s<addon>%s<name>`", reference, sep, sep)
+ end
+ local payloaddir = addon._payloaddir(addonname, kind)
+ if not payloaddir then
+ return nil, string.format("%s not found!\nplease install the addon which provides it first: xmake addon --install %s", reference, addonname)
+ end
+ return {dir = payloaddir, name = name, addon = addonname}
+end
+
+-- get the registry of the installed addons
+--
+-- an addon can be installed with several versions at the same time, e.g. the projects
+-- may lock the different versions of it, so we save all of them
+--
+-- @return the registry, e.g. {["esp32"] = {active = "1.0.3", versions = {["1.0.3"] = {...}}}}
+--
+function addon._registry(opt)
+ local registry = addon._REGISTRY
+ if opt and opt.force then
+ registry = nil
+ end
+ if registry == nil then
+ registry = {}
+ local registryfile = addon._registryfile()
+ if os.isfile(registryfile) then
+ registry = io.load(registryfile) or {}
+ end
+ -- migrate the old registry, it only saved one version for each addon
+ for dirname, addoninfo in pairs(registry) do
+ if addoninfo.versions == nil then
+ registry[dirname] = {active = addoninfo.version,
+ versions = {[addoninfo.version] = addoninfo}}
+ end
+ end
+ addon._REGISTRY = registry
+ addon._ADDONS = nil
+ end
+ return registry
+end
+
+-- pin the active version of the given addon for this process
+--
+-- @note a project locks the versions of its addons, so we need to activate them
+-- when we load it, @see core/project/addons.lua
+--
+function addon.pin(name, version)
+ local pinned = addon._PINNED
+ if pinned == nil then
+ pinned = {}
+ addon._PINNED = pinned
+ end
+ pinned[addon.dirname(name)] = version
+ addon._ADDONS = nil
+end
+
+-- get all the installed versions of the given addon, e.g. {"1.0.2", "1.0.3"}
+function addon.versions(name)
+ local addoninfo = addon._registry()[addon.dirname(name)]
+ return table.orderkeys(addoninfo and addoninfo.versions or {})
+end
+
+-- get all installed addons, only the active version of each addon
+--
+-- @param opt the options, e.g. {force = true}, we need it to reload the registry
+-- if the addons have been installed by another process
+--
+-- @return the addons table, e.g. {["hello-world"] = {version = "latest", payloads = {"plugins"}}}
+--
+function addon.addons(opt)
+ opt = opt or {}
+ if opt.force then
+ addon._registry({force = true})
+ end
+
+ -- get the really installed versions instead of the pinned ones? e.g. locking them
+ -- @note we do not cache it, the cache is the pinned view of this project
+ if opt.unpinned then
+ local addons = {}
+ for dirname, addoninfo in pairs(addon._registry()) do
+ local versioninfo = addoninfo.versions and addoninfo.versions[addoninfo.active]
+ if versioninfo then
+ addons[dirname] = versioninfo
+ end
+ end
+ return addons
+ end
+
+ local addons = addon._ADDONS
+ if addons == nil then
+ addons = {}
+ local pinned = addon._PINNED or {}
+ for dirname, addoninfo in pairs(addon._registry()) do
+ -- the project may lock another version of it, @see addon.pin
+ local version = pinned[dirname] or addoninfo.active
+ local versioninfo = addoninfo.versions and addoninfo.versions[version]
+ if versioninfo then
+ addons[dirname] = versioninfo
+ end
+ end
+ addon._ADDONS = addons
+ end
+ return addons
+end
+
+-- get the install directory of the given addon, e.g. ~/.xmake/addons/<name>/<version>
+function addon.addondir(name, version)
+ local dirname = addon.dirname(name)
+ if version == nil then
+ local addoninfo = addon.addons()[dirname]
+ if addoninfo == nil then
+ return nil
+ end
+ version = addoninfo.version
+ end
+ return path.join(addon.installdir(), dirname, version)
+end
+
+-- get the modules which the installed addons export as the global modules
+--
+-- they are declared in the addon manifest, e.g. add_globalmodules("core.tools.esptool"),
+-- so that they can be imported with their plain names by the internal calls,
+-- e.g. import("core.tools.esptool"), find_tool("esptool")
+--
+-- @return the modules table, e.g. {["core.tools.esptool"] = "~/.xmake/addons/esp32/v1.0.0/modules"}
+--
+function addon.globalmodules()
+ local globalmodules = addon._GLOBALMODULES
+ if globalmodules == nil then
+ globalmodules = {}
+ for dirname, addoninfo in pairs(addon.addons()) do
+ for _, name in ipairs(addoninfo.globalmodules or {}) do
+ globalmodules[name] = path.join(addon.installdir(), dirname, addoninfo.version, "modules")
+ end
+ end
+ addon._GLOBALMODULES = globalmodules
+ end
+ return globalmodules
+end
+
+-- find the include files of the given addon reference, e.g. includes("@addon/esp32/board")
+--
+-- @param interp the interpreter which is loading the file, @see interpreter:includes_resolver_add
+-- @param reference the reference, e.g. "@addon/esp32/board", "@self/board"
+--
+-- @return the files, or nil and errors
+--
+function addon.find_includes(interp, reference)
+ if not addon.is_reference(reference, "/") then
+ return
+ end
+ local referenceinfo, errors = addon.resolve_reference(reference, "/", "includes", {scriptdir = interp:scriptdir()})
+ if not referenceinfo then
+ return nil, errors
+ end
+ local name = referenceinfo.name
+ local files
+ if name:endswith(".lua") then
+ files = os.files(path.join(referenceinfo.dir, name))
+ else
+ files = os.files(path.join(referenceinfo.dir, name, "xmake.lua"))
+ end
+
+ -- the addon is installed, but it does not provide this file, we cannot ignore it
+ if not files or #files == 0 then
+ os.raise("includes(%s) not found!", reference)
+ end
+ return files
+end
+
+-- get the payload directories of the given kind from all installed addons
+--
+-- @param kind the payload kind, e.g. "plugins", "rules"
+-- @return the directories, e.g. {"~/.xmake/addons/hello-world/latest/plugins"}
+--
+-- @note we do not check if these directories exist, the callers will just ignore the invalid ones
+--
+function addon.payloads(kind)
+ local payloads = {}
+ for _, payloadinfo in ipairs(addon.payloadinfos(kind)) do
+ table.insert(payloads, payloadinfo.dir)
+ end
+ return payloads
+end
+
+-- get the payload information of the given kind from all installed addons
+--
+-- @param kind the payload kind, e.g. "plugins", "rules"
+-- @return the payload infos, e.g. {{name = "hello-world", version = "latest", dir = "~/.xmake/addons/hello-world/latest/plugins"}}
+--
+function addon.payloadinfos(kind)
+ local payloadinfos = {}
+ for name, addoninfo in table.orderpairs(addon.addons()) do
+ if table.contains(addoninfo.payloads or {}, kind) then
+ table.insert(payloadinfos, {
+ name = name,
+ version = addoninfo.version,
+ dir = path.join(addon.installdir(), name, addoninfo.version, kind)})
+ end
+ end
+ return payloadinfos
+end
+
+-- get the payload root directory of the given addon source directory
+--
+-- an addon repository has its own files, e.g. tests, ci scripts and documents,
+-- so its payloads can be placed in the `src` subdirectory, and we only install them
+--
+-- e.g.
+-- esp32-devel/src/{plugins,rules,toolchains,templates} -- with the `src` layout
+-- hello-world/{plugins} -- without it, for the simple addons
+--
+-- @param sourcedir the addon source directory
+-- @return the payload root directory, it will be nil if no payload is found
+--
+function addon.payloadroot(sourcedir)
+
+ -- the addon can set its payload root directory explicitly, e.g. set_sourcedir("src")
+ local manifest = addon.manifest(sourcedir)
+ if manifest and manifest.sourcedir then
+ local payloadroot = path.join(sourcedir, manifest.sourcedir)
+ if #addon.payloads_of(payloadroot) > 0 then
+ return payloadroot
+ end
+ return
+ end
+
+ local srcdir = path.join(sourcedir, "src")
+ if #addon.payloads_of(srcdir) > 0 then
+ return srcdir
+ end
+ if #addon.payloads_of(sourcedir) > 0 then
+ return sourcedir
+ end
+end
+
+-- get the payload directories of the given addon directory, e.g. {"plugins", "rules"}
+function addon.payloads_of(addondir)
+ local payloads = {}
+ for _, payloaddir in ipairs(addon._payloaddirs()) do
+ if os.isdir(path.join(addondir, payloaddir)) then
+ table.insert(payloads, payloaddir)
+ end
+ end
+ return payloads
+end
+
+-- get the default on_install script of addon packages
+--
+-- we only install the payload directories of this addon, e.g. plugins, rules, toolchains, ...
+--
+function addon.installscript()
+ return function (package)
+ local sourcedir = os.curdir()
+
+ -- the addon name is its identity, e.g. the install directory, the registry key
+ -- and the `@addon/<name>/xxx` references, so the package must be distributed with the same name
+ local manifest, errors = addon.manifest(sourcedir)
+ if errors then
+ os.raise(errors)
+ end
+ if manifest and addon.dirname(manifest.name) ~= addon.dirname(package:name()) then
+ os.raise("addon(%s) does not match the package name(%s) in the repository!\nplease fix the package recipe or the addon manifest.",
+ manifest.name, package:name())
+ end
+
+ local payloadroot = addon.payloadroot(sourcedir)
+ if not payloadroot then
+ os.raise("addon(%s): no payload directory found, e.g. plugins!", package:name())
+ end
+ for _, payloaddir in ipairs(addon.payloads_of(payloadroot)) do
+ os.cp(path.join(payloadroot, payloaddir), package:installdir())
+ end
+
+ -- we need not install the manifest, the package manifest(manifest.txt) and the addons
+ -- registry already have all the information, we just pass it to the registration
+ if manifest then
+ package:data_set("addon.manifest", manifest)
+ end
+ end
+end
+
+-- register the given installed addon
+--
+-- @param name the addon name
+-- @param version the addon version, e.g. "1.0.1", "latest"
+-- @param opt the options, e.g. {description = "...", deps = {"foo"}}
+--
+-- @note the addon manifest(addon.lua) is only read when installing, everything which
+-- is needed later is recorded here, so we never parse it again
+--
+-- @return true or false and errors
+--
+function addon.register(name, version, opt)
+ opt = opt or {}
+ local dirname = addon.dirname(name)
+ local addondir = path.join(addon.installdir(), dirname, version)
+ local addoninfo = {version = version,
+ -- we need to keep the raw name, the directory name is only its
+ -- normalized form, e.g. "myns::foo" -> "myns_foo"
+ name = name ~= dirname and name or nil,
+ description = opt.description,
+ deps = opt.deps,
+ -- where it comes from, e.g. {url = ..., commit = ..., branch = ...}
+ repo = opt.repo,
+ -- the deps which the addon itself declares in its manifest, they are
+ -- recorded whenever this addon has one, so that the repositories can
+ -- check that the manifest and the package recipe are kept in sync
+ manifest_deps = opt.manifest_deps,
+ globalmodules = opt.globalmodules,
+ payloads = addon.payloads_of(addondir),
+ plugins = addon._plugins_of(addondir),
+ templates = addon._templates_of(addondir)}
+
+ -- we need to check the conflicts of the plugins and templates first,
+ -- they are not namespaced and we do not know which one will be used
+ local errors = addon._check_conflicts(dirname, addoninfo)
+ if errors then
+ return false, errors
+ end
+
+ -- we can install several versions of an addon at the same time,
+ -- and the version which we install now is always the active one
+ local registry = addon._registry()
+ local entry = registry[dirname]
+ if entry == nil or entry.versions == nil then
+ entry = {versions = {}}
+ registry[dirname] = entry
+ end
+ entry.versions[version] = addoninfo
+ entry.active = version
+ addon._save(registry)
+ return true
+end
+
+-- remove the given installed addon
+--
+-- @param name the addon name
+-- @return true or false and errors
+--
+function addon.remove(name, opt)
+ opt = opt or {}
+ local dirname = addon.dirname(name)
+ local installdir = path.join(addon.installdir(), dirname)
+ if not os.isdir(installdir) then
+ return false, string.format("addon(%s) not found!", name)
+ end
+
+ -- we cannot remove it if the other addons depend on it
+ if not opt.force then
+ local parents = addon._parents(name)
+ if parents then
+ return false, string.format("addon(%s) cannot be removed, it's depended on by the addon(%s)!\nplease remove them first, or pass --force to remove it anyway",
+ name, table.concat(parents, ", "))
+ end
+ end
+ -- we need to remove the symlinks first, we cannot remove them recursively,
+ -- otherwise the linked files would be removed too
+ --
+ -- e.g. the user may link the install directory to the addon source directory when developing it
+ for _, versiondir in ipairs(os.dirs(path.join(installdir, "*"))) do
+ if os.islink(versiondir) then
+ os.rmfile(versiondir)
+ end
+ end
+ if os.islink(installdir) then
+ return os.rmfile(installdir)
+ end
+
+ local ok, errors = os.rm(installdir)
+ if not ok then
+ return false, errors
+ end
+ addon.unregister(name)
+ return true
+end
+
+-- reload the addons registry and the caches which are built from it
+--
+-- @note we need it if the addons have been installed by another process,
+-- e.g. the addons which a project declares, @see core/project/project.lua
+--
+function addon.reload()
+ addon._REGISTRY = nil
+ addon._ADDONS = nil
+ addon._MANIFESTS = nil
+ addon._GLOBALMODULES = nil
+end
+
+-- return module
+return addon
diff --git a/xmake/core/package/component.lua b/xmake/core/package/component.lua
index c55dc4a73..bdf1ff061 100644
--- a/xmake/core/package/component.lua
+++ b/xmake/core/package/component.lua
@@ -135,7 +135,7 @@ function _instance:_load()
if not loaded then
local script = self:_on_component()
if script then
- local ok, errors = sandbox.load(script, self:package(), self)
+ local ok, errors = sandbox.call(script, self:package(), self)
if not ok then
os.raise("load component(%s) failed, %s", self:name(), errors or "unknown errors")
end
diff --git a/xmake/core/package/package.lua b/xmake/core/package/package.lua
index 0d164ce95..3d98a3743 100644
--- a/xmake/core/package/package.lua
+++ b/xmake/core/package/package.lua
@@ -37,6 +37,7 @@ local interpreter = require("base/interpreter")
local select_script = require("base/private/select_script")
local is_cross = require("base/private/is_cross")
local memcache = require("cache/memcache")
+local addon = require("package/addon")
local toolchain = require("tool/toolchain")
local compiler = require("tool/compiler")
local linker = require("tool/linker")
@@ -54,6 +55,16 @@ local sandbox_os = require("sandbox/modules/os")
local sandbox_module = require("sandbox/modules/import/core/sandbox/module")
-- new an instance
+--
+-- @param name the package name, the namespace prefix will be stripped and saved separately,
+-- e.g. "zlib", "myns::zlib", but "vcpkg::zlib" will be kept as a whole,
+-- because `vcpkg` is a package manager, but not a namespace
+-- @param info the package description scope info
+-- @param opt the options
+-- - scriptdir: the directory of the package description file, the relative paths in
+-- this package will be relative to it
+-- - repo: the repository instance which this package belongs to
+--
function _instance.new(name, info, opt)
opt = opt or {}
local instance = table.inherit(_instance)
@@ -610,6 +621,23 @@ function _instance:is_toolchain()
return self:kind() == "toolchain"
end
+-- is addon package?
+--
+-- it will be installed to `~/.xmake/addons/<name>/<version>` and
+-- it can provide plugins, rules, toolchains, templates and modules for xmake
+--
+function _instance:is_addon()
+ return self:kind() == "addon"
+end
+
+-- is plugin package?
+--
+-- @note this kind is deprecated, please use the `addon` kind instead
+--
+function _instance:is_plugin()
+ return self:kind() == "plugin"
+end
+
-- is library package?
--
-- @return true if the package kind is "library" or default
@@ -907,20 +935,32 @@ function _instance:installdir(...)
installdir = self:get("installdir")
if not installdir then
local name = self:name():lower():gsub("::", "_")
- if self:is_local() then
- installdir = path.join(package.installdir({localdir = true}), name:sub(1, 1):lower(), name)
- else
- installdir = path.join(package.installdir(), name:sub(1, 1):lower(), name)
- end
- local version_str = self:version_str()
- if version_str then
- -- strip invalid characters on windows, e.g. `>= <=`
+ if self:is_addon() then
+ -- e.g. ~/.xmake/addons/<name>/<version>
+ local version_str = self:version_str() or "latest"
if os.is_host("windows") then
version_str = version_str:gsub("[>=<|%*]", "")
end
- installdir = path.join(installdir, version_str)
+ installdir = addon.addondir(self:name(), version_str)
+ elseif self:is_plugin() then
+ -- deprecated, @see the `addon` kind
+ installdir = path.join(global.directory(), "plugins", name)
+ else
+ if self:is_local() then
+ installdir = path.join(package.installdir({localdir = true}), name:sub(1, 1):lower(), name)
+ else
+ installdir = path.join(package.installdir(), name:sub(1, 1):lower(), name)
+ end
+ local version_str = self:version_str()
+ if version_str then
+ -- strip invalid characters on windows, e.g. `>= <=`
+ if os.is_host("windows") then
+ version_str = version_str:gsub("[>=<|%*]", "")
+ end
+ installdir = path.join(installdir, version_str)
+ end
+ installdir = path.join(installdir, self:buildhash())
end
- installdir = path.join(installdir, self:buildhash())
end
self._INSTALLDIR = installdir
end
@@ -1136,7 +1176,7 @@ function _instance:_load()
if on_load then
on_load(self)
end
-
+
-- load all components
self:_load_components()
@@ -1174,6 +1214,7 @@ function _instance:_rawenvs()
envs.DYLD_LIBRARY_PATH = {"lib"}
end
end
+
self._RAWENVS = envs
end
return envs
@@ -2076,11 +2117,17 @@ function _instance:fetch(opt)
-- always install to the local project directory?
-- @see https://github.com/xmake-io/xmake/pull/4376
+ --
+ -- @note the host packages are the tools which build the other packages, e.g. the toolchains,
+ -- they do not depend on the project configuration and they are shared between the projects,
+ -- so they are only installed locally with their own policy
+ -- @see https://github.com/xmake-io/xmake/issues/7716
+ local policyname = self:is_host() and "package.host.install_locally" or "package.install_locally"
local install_locally
- if project and project.policy("package.install_locally") then
+ if project and project.policy(policyname) then
install_locally = true
end
- if install_locally == nil and self:policy("package.install_locally") then
+ if install_locally == nil and self:policy(policyname) then
install_locally = true
end
if not self:is_local() and install_locally and system ~= true then
@@ -2488,9 +2535,20 @@ function _instance:_generate_runtime_configs(sourcekind)
self.sourcekinds = function (self)
return sourcekind
end
- configs.cxflags = self:compiler(sourcekind):map_flags("runtime", runtimes, {target = self})
- configs.ldflags = self:linker("binary", sourcekind):map_flags("runtime", runtimes, {target = self})
- configs.shflags = self:linker("shared", sourcekind):map_flags("runtime", runtimes, {target = self})
+ local cxflags = self:compiler(sourcekind):map_flags("runtime", runtimes, {target = self})
+ local ldflags = self:linker("binary", sourcekind):map_flags("runtime", runtimes, {target = self})
+ local shflags = self:linker("shared", sourcekind):map_flags("runtime", runtimes, {target = self})
+
+ -- @note the multi-argument flags must be checked as a whole, e.g. the clang runtime
+ -- flags on windows, `-Xclang --dependent-lib=xxx` would be broken if
+ -- `--dependent-lib=xxx` is checked and dropped separately
+ -- @see https://github.com/xmake-io/xmake/issues/7704
+ local group = function (flags)
+ return flags and #flags > 1 and {table.wrap_lock(flags)} or flags
+ end
+ configs.cxflags = group(cxflags)
+ configs.ldflags = group(ldflags)
+ configs.shflags = group(shflags)
self.sourcekinds = nil
end
return configs
@@ -2556,6 +2614,79 @@ function _instance:_generate_build_configs(configs, opt)
return configs
end
+-- has the given payloads? (only for the addon packages)
+--
+-- @param opt the payloads to be checked, the value can be a string or a list, e.g.
+-- {rules = "app", toolchains = "esp32", plugins = "monitor",
+-- templates = "c/esp32.hello", modules = {"private.board", "private.flasher"}}
+--
+-- @return true, or false and errors
+--
+-- e.g.
+--
+-- on_test(function (package)
+-- assert(package:has_addon({rules = "app", toolchains = "esp32"}))
+-- end)
+--
+function _instance:has_addon(opt)
+ if not self:is_addon() then
+ return false, string.format("package(%s) is not an addon!", self:name())
+ end
+
+ -- the addon should be registered after installing it
+ local addoninfo = addon.addons()[addon.dirname(self:name())]
+ if not addoninfo then
+ return false, string.format("addon(%s) is not installed!", self:name())
+ end
+ if opt == nil then
+ return true
+ end
+
+ local checkers = {}
+
+ -- the rules are namespaced, e.g. @addon/esp32-devel/app
+ --
+ -- @note we need to reload the global rules, this addon may be installed just now
+ checkers.rules = function (name)
+ local rule = require("project/rule")
+ rule.clear()
+ return rule.rules()["@addon/" .. self:name() .. "/" .. name] ~= nil
+ end
+
+ -- the toolchains are namespaced too, e.g. @addon/esp32-devel/esp32
+ checkers.toolchains = function (name)
+ return toolchain.load("@addon/" .. self:name() .. "/" .. name) ~= nil
+ end
+
+ -- the plugins and the templates are not namespaced, we get them from the addons registry,
+ -- the task list of this process has been loaded before installing this addon
+ checkers.plugins = function (name)
+ return table.contains(addoninfo.plugins or {}, name)
+ end
+ checkers.templates = function (name)
+ return table.contains(addoninfo.templates or {}, name)
+ end
+
+ -- the modules are files, e.g. modules/serial.lua, modules/private/board.lua
+ checkers.modules = function (name)
+ local modulepath = path.join(self:installdir(), "modules", (name:gsub("%.", "/")))
+ return os.isfile(modulepath .. ".lua") or os.isdir(modulepath)
+ end
+
+ for kind, names in pairs(opt) do
+ local checker = checkers[kind]
+ if not checker then
+ return false, string.format("unknown addon payload(%s), it should be one of rules, toolchains, plugins, templates and modules!", kind)
+ end
+ for _, name in ipairs(table.wrap(names)) do
+ if not checker(name) then
+ return false, string.format("%s(%s) not found in the addon(%s)!", kind, name, self:name())
+ end
+ end
+ end
+ return true
+end
+
-- has the given c funcs?
--
-- @param funcs the funcs
@@ -2985,12 +3116,20 @@ end
--
-- @param opt the options, e.g. {localdir = true}
-- - localdir: return the local project packages directory (build/.packages)
--- instead of the global directory (~/.xmake/packages)
+-- instead of the global directory (~/.xmake/packages),
+-- it can be overridden with `XMAKE_PKG_LOCALDIR`
--
-- @return the install directory path
--
function package.installdir(opt)
if opt and opt.localdir then
+ -- the parent process passes its local directory to the sub-process which builds
+ -- a package, so the packages it installs locally land in the same place and are
+ -- not installed twice, @see https://github.com/xmake-io/xmake/issues/7716
+ local localdir = os.getenv("XMAKE_PKG_LOCALDIR")
+ if localdir then
+ return path.normalize(path.absolute(localdir))
+ end
return path.join(config.builddir({absolute = true}), ".packages")
end
local installdir = package._INSTALLDIR
@@ -3015,6 +3154,14 @@ function package.searchdirs()
end
-- load the package from the system directories
+--
+-- it will be used for `add_requires("zlib", {system = true})` and the 3rd package managers,
+-- e.g. add_requires("vcpkg::zlib"), add_requires("conan::zlib/1.2.11")
+--
+-- @param packagename the package name, e.g. "zlib", "vcpkg::zlib", "xmake::zlib"
+--
+-- @return the package instance and errors
+--
function package.load_from_system(packagename)
-- get package info
@@ -3083,6 +3230,15 @@ function package.load_from_system(packagename)
end
-- load the package from the project file
+--
+-- it will load the package which is defined by `package()` in the project xmake.lua,
+-- and we will also try to find it from the project namespaces if it's not found directly
+--
+-- @param packagename the package name, e.g. "zlib", it can be without the namespace prefix
+-- @param project the project module, we need to pass it to avoid the cyclic imports
+--
+-- @return the package instance and errors, it will be nil if this package is not defined in the project
+--
function package.load_from_project(packagename, project)
-- load packages (with cache)
@@ -3111,8 +3267,20 @@ function package.load_from_project(packagename, project)
end
-- load the package from the package directory or package description file
+--
+-- @param packagename the package name, e.g. "zlib"
+-- @param packagedir the package directory, we will load `packagedir/xmake.lua`, it can be nil if `opt.packagefile` is set
+-- @param opt the options
+-- - packagefile: load the package from the given description file directly instead of `packagedir/xmake.lua`
+-- - plat: the given platform, we need to set it to the description scope at same time,
+-- e.g. add_requires("zlib~mingw", {plat = "mingw"})
+-- @see https://github.com/orgs/xmake-io/discussions/3439
+-- - arch: the given architecture, it's the same as `opt.plat`
+-- - repo: the repository instance which this package belongs to
+--
+-- @return the package instance and errors
+--
function package.load_from_repository(packagename, packagedir, opt)
-
opt = opt or {}
-- find the package script path
@@ -3173,6 +3341,21 @@ function package.load_from_repository(packagename, packagedir, opt)
return nil, string.format("%s: package(%s) not found!", scriptpath, packagename)
end
+ -- we need set the default on_install script if it's addon package
+ if packageinfo:get("kind") == "addon" and not packageinfo:get("install") then
+ packageinfo:set("install", addon.installscript())
+ end
+
+ -- we need set the default on_install script if it's plugin package
+ -- @note the plugin kind is deprecated, please use the addon kind instead
+ if packageinfo:get("kind") == "plugin" and not packageinfo:get("install") then
+ -- only one code line, we can directly omit the sandbox wrapper.
+ local on_install = function (pkg)
+ os.cp("*", pkg:installdir())
+ end
+ packageinfo:set("install", on_install)
+ end
+
package._memcache():set2("packageinfos.repository", cachekey, packageinfo)
end
diff --git a/xmake/core/platform/menu.lua b/xmake/core/platform/menu.lua
index cec901ee8..77a57381d 100644
--- a/xmake/core/platform/menu.lua
+++ b/xmake/core/platform/menu.lua
@@ -42,7 +42,7 @@ function _remote_build_is_connected()
local projectdir = os.projectdir()
local projectfile = os.projectfile()
if projectfile and os.isfile(projectfile) and projectdir then
- local workdir = path.join(config.directory(), "remote_build")
+ local workdir = path.join(config.directory(), "service", "remote_build")
local statusfile = path.join(workdir, "status.txt")
if os.isfile(statusfile) then
local status = io.load(statusfile)
diff --git a/xmake/core/platform/platform.lua b/xmake/core/platform/platform.lua
index f5eb9ab94..cd6c7722e 100644
--- a/xmake/core/platform/platform.lua
+++ b/xmake/core/platform/platform.lua
@@ -109,7 +109,7 @@ function _instance:get(name)
if not self._LOADED and not self:_is_builtin_conf(name) then
local on_load = self._INFO:get("load")
if on_load then
- local ok, errors = sandbox.load(on_load, self)
+ local ok, errors = sandbox.call(on_load, self)
if not ok then
os.raise(errors)
end
diff --git a/xmake/core/project/addons.lua b/xmake/core/project/addons.lua
new file mode 100644
index 000000000..c979a6f8d
--- /dev/null
+++ b/xmake/core/project/addons.lua
@@ -0,0 +1,140 @@
+--!A cross-platform build utility based on Lua
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+-- Copyright (C) 2015-present, Xmake Open Source Community.
+--
+-- @author ruki
+-- @file addons.lua
+--
+
+-- define module
+local addons = addons or {}
+
+-- load modules
+local os = require("base/os")
+local io = require("base/io")
+local path = require("base/path")
+local table = require("base/table")
+local semver = require("base/semver")
+local addon = require("package/addon")
+
+-- the lock file of the declared addons, e.g. <projectdir>/xmake-addons.lock
+--
+-- @note it's independent of `xmake-requires.lock`, the addons are always locked,
+-- they provide the build scripts and we should never change them silently
+--
+function addons.lockfile(projectdir)
+ return path.join(projectdir or os.projectdir(), "xmake-addons.lock")
+end
+
+-- get the format version of the addons lock file
+--
+-- @see xmake/core/project/project.lua, project.requireslock_version()
+--
+function addons.lockfile_version()
+ return "1.0"
+end
+
+-- check the addons which a project declares, e.g. add_addons("esp32-devel 1.0.x")
+--
+-- @return true, or false and errors
+--
+function addons.validate(requires)
+ local declared = {}
+ for _, requirestr in ipairs(requires) do
+ local name = addons.requirename(requirestr)
+ if name == "addon" or name == "self" then
+ return false, string.format("add_addons(%s): the name is reserved by xmake for the addon references, please rename it!", name)
+ end
+ if name == "." or name == ".." or name:find("[/\\:]") then
+ return false, string.format("add_addons(%s): invalid addon name!", name)
+ end
+
+ -- we can only install one version of an addon for a project
+ if declared[name] then
+ return false, string.format("add_addons(%s): it is declared twice, e.g. `%s` and `%s`, please merge them!",
+ name, declared[name], requirestr)
+ end
+ declared[name] = requirestr
+ end
+ return true
+end
+
+-- split the given declaration into the name and the version range
+--
+-- @note the version range can contain spaces, so everything after the name belongs to it,
+-- e.g. "esp32-devel 1.0.x", "esp32-devel >=1.0 <2.0", "esp32-devel master || >1.4",
+-- @see xmake/modules/private/utils/package.lua
+--
+function addons.requirename(requirestr)
+ local splitinfo = requirestr:split("%s+", {limit = 2})
+ return splitinfo[1], splitinfo[2]
+end
+
+-- is the locked version still valid for the given declaration?
+--
+-- @note the declaration is authoritative, the lock only pins a version inside it,
+-- so we need to resolve it again if the user has changed the declared version
+--
+function addons.locked_valid(requirestr, lockinfo)
+ if not lockinfo or not lockinfo.version then
+ return false
+ end
+ local _, range = addons.requirename(requirestr)
+ if range then
+ -- @note we can only compare the semantic versions, e.g. the local addons are always `latest`
+ return semver.is_valid(lockinfo.version) and semver.satisfies(lockinfo.version, range)
+ end
+ return true
+end
+
+-- get the locked addons, e.g. {["esp32-devel"] = {version = "1.0.3"}}
+function addons.locked(projectdir)
+ local lockfile = addons.lockfile(projectdir)
+ if os.isfile(lockfile) then
+ local lockinfo = io.load(lockfile)
+ if lockinfo then
+ lockinfo.__meta__ = nil
+ return lockinfo
+ end
+ end
+end
+
+-- are all the declared addons installed already?
+--
+-- @note we need to check it in-process for every command which loads the project,
+-- so we only check the locked versions here, the installer will resolve them again
+--
+function addons.satisfied(requires, projectdir)
+ local locked = addons.locked(projectdir)
+ if not locked then
+ return false
+ end
+ local installed = addon.addons()
+ for _, requirestr in ipairs(requires) do
+ local name = addons.requirename(requirestr)
+ local lockinfo = locked[name]
+ if not addons.locked_valid(requirestr, lockinfo) then
+ return false
+ end
+ local addoninfo = installed[addon.dirname(name)]
+ if not addoninfo or addoninfo.version ~= lockinfo.version then
+ return false
+ end
+ end
+ return true
+end
+
+-- return module
+return addons
diff --git a/xmake/core/project/option.lua b/xmake/core/project/option.lua
index 62687a106..43ba10f05 100644
--- a/xmake/core/project/option.lua
+++ b/xmake/core/project/option.lua
@@ -142,7 +142,7 @@ function _instance:_do_check_cxsnippets(snippets)
-- check snippets (run with output)
if #table.keys(snippets_output) > 0 then
- local ok, results_or_errors, output = sandbox.load(self._check_cxsnippets, snippets_output, {
+ local ok, results_or_errors, output = sandbox.call(self._check_cxsnippets, snippets_output, {
target = self,
sourcekind = sourcekind,
types = types,
@@ -165,7 +165,7 @@ function _instance:_do_check_cxsnippets(snippets)
-- check snippets (run only)
if passed == 0 and #table.keys(snippets_tryrun) > 0 then
- local ok, results_or_errors = sandbox.load(self._check_cxsnippets, snippets_tryrun, {
+ local ok, results_or_errors = sandbox.call(self._check_cxsnippets, snippets_tryrun, {
target = self,
sourcekind = sourcekind,
types = types,
@@ -187,7 +187,7 @@ function _instance:_do_check_cxsnippets(snippets)
-- check snippets (run with binary_match)
if #table.keys(snippets_binary_match) > 0 then
- local ok, results_or_errors, output = sandbox.load(self._check_cxsnippets, snippets_binary_match, {
+ local ok, results_or_errors, output = sandbox.call(self._check_cxsnippets, snippets_binary_match, {
target = self,
sourcekind = sourcekind,
types = types,
@@ -209,7 +209,7 @@ function _instance:_do_check_cxsnippets(snippets)
-- check snippets (build only)
if passed == 0 or #table.keys(snippets_build) > 0 then
- local ok, results_or_errors = sandbox.load(self._check_cxsnippets, snippets_build, {
+ local ok, results_or_errors = sandbox.call(self._check_cxsnippets, snippets_build, {
target = self,
sourcekind = sourcekind,
types = types,
@@ -296,7 +296,7 @@ function _instance:_on_check()
-- get check script
local check = self:script("check")
if check then
- return sandbox.load(check, self)
+ return sandbox.call(check, self)
else
return self:_do_check()
end
diff --git a/xmake/core/project/policy.lua b/xmake/core/project/policy.lua
index 617a792d6..bcda37858 100644
--- a/xmake/core/project/policy.lua
+++ b/xmake/core/project/policy.lua
@@ -83,6 +83,8 @@ function policy.policies()
["build.c++.modules.hide_dependencies"] = {description = "Hide dependencies from the commandline when build C++ modules.", default = false, type = "boolean"},
-- Enable two phase compilation for C++ modules if supported by the compiler
["build.c++.modules.two_phases"] = {description = "Enable two phase compilation if supported.", default = true, type = "boolean"},
+ -- Use --precompile-reduced-bmi for Clang two-phase module compilation if supported
+ ["build.c++.modules.clang.precompile_reduced_bmi"] = {description = "Use --precompile-reduced-bmi for Clang two-phase module compilation if supported.", default = false, type = "boolean"},
-- Enable std module
["build.c++.modules.std"] = {description = "Enable std modules.", default = true, type = "boolean"},
-- Enable unreferenced and non-public named module culling
@@ -158,6 +160,12 @@ function policy.policies()
["package.install_always"] = {description = "Always install packages every time.", type = "boolean"},
-- Install packages in the local project folder
["package.install_locally"] = {description = "Install packages in the local project folder.", default = false, type = "boolean"},
+ -- Install the host packages in the local project folder
+ --
+ -- the host packages are the tools which build the other packages, e.g. the toolchains,
+ -- they do not depend on the project configuration and they are shared between the projects,
+ -- so they have their own policy, @see https://github.com/xmake-io/xmake/issues/7716
+ ["package.host.install_locally"] = {description = "Install the host packages in the local project folder.", default = false, type = "boolean"},
-- Keep package source code after installing (disable source dir cleanup)
["package.keep_source"] = {description = "Keep package source code after installing.", default = false, type = "boolean"},
-- Set custom headers when downloading package
diff --git a/xmake/core/project/project.lua b/xmake/core/project/project.lua
index d1030f130..b92c5cfab 100644
--- a/xmake/core/project/project.lua
+++ b/xmake/core/project/project.lua
@@ -45,6 +45,8 @@ local option = require("project/option")
local policy = require("project/policy")
local project_package = require("project/package")
local deprecated_project = require("project/deprecated/project")
+local addon = require("package/addon")
+local addons = require("project/addons")
local package = require("package/package")
local platform = require("platform/platform")
local toolchain = require("tool/toolchain")
@@ -227,11 +229,131 @@ function project._api_add_toolchaindirs(interp, ...)
end
end
+-- install the addons which this project declares
+--
+-- @note we cannot install them here, we are loading the project, so we do it in a
+-- sub-process, @see xmake/modules/private/action/addon/impl/install_addons.lua
+--
+function project._install_addons(rootinfo)
+ -- @note we need to cache the result, the project may be loaded many times,
+ -- otherwise the failure would be ignored by the next load
+ local result = project._ADDONS_RESULT
+ if result == nil then
+ result = project._do_install_addons(rootinfo)
+ project._ADDONS_RESULT = result
+ end
+ return result
+end
+
+-- activate the addon versions which this project locks
+--
+-- @note an addon can be installed with several versions at the same time, the other
+-- projects may lock the other versions of it, @see core/package/addon.lua
+--
+function project._pin_addons()
+ for name, lockinfo in pairs(addons.locked() or {}) do
+ -- @note we can only pin an installed version, otherwise this addon would be
+ -- invisible and every reference to it would just say `not found`
+ --
+ -- the locked version is installed by the auto-fetch, @see addons.satisfied()
+ --
+ if lockinfo.version and table.contains(addon.versions(name), lockinfo.version) then
+ addon.pin(name, lockinfo.version)
+ end
+ end
+end
+
+-- do install the addons which this project declares
+-- install the addons which this project declares, e.g. add_addons("esp32-devel 1.0.x")
+--
+-- @return the result, e.g. {ok = true, installed = true}, {ok = false, errors = ".."}
+--
+function project._do_install_addons(rootinfo)
+
+ -- this project declares nothing?
+ local requires = table.wrap(rootinfo:get("addons"))
+ if #requires == 0 then
+ return {ok = true}
+ end
+ local ok, errors = addons.validate(requires)
+ if not ok then
+ return {ok = false, errors = errors}
+ end
+
+ -- they have been installed already?
+ if addons.satisfied(requires) then
+ return {ok = true}
+ end
+
+ -- tell the user why we are installing something, it may need to confirm and download,
+ -- e.g. `xmake --help` in a project directory which declares some addons
+ utils.cprint("${color.warning}note: ${clear}this project needs the addons(${bright}%s${clear}), installing them ..",
+ table.concat(requires, ", "))
+ if baseoption.get("help") then
+ -- the help menu also shows the options which the addons provide, but the user
+ -- did not ask for an installation, so we tell them how to skip it
+ utils.cprint("${dim}we can run it outside of the project directory to skip the installation${clear}")
+ end
+
+ -- we pass the declarations to the installer, it must not load this project again,
+ -- @see xmake/modules/private/action/addon/impl/install_addons.lua
+ local datafile = os.tmpfile()
+ local ok, errors = io.save(datafile, {addons = requires, repositories = table.wrap(rootinfo:get("repositories"))})
+ if not ok then
+ return {ok = false, errors = errors}
+ end
+
+ -- @note we run it in a working directory which has no project, @see addon.workdir(),
+ -- otherwise it would load this project again
+ --
+ -- @note we may be called when building the option menu, the command line has not
+ -- been parsed yet, so we can only get the common flags from the raw arguments
+ --
+ local argv = {"lua"}
+ local flags = {["-y"] = "--yes", ["--yes"] = "--yes",
+ ["-v"] = "--verbose", ["--verbose"] = "--verbose",
+ ["-D"] = "--diagnosis", ["--diagnosis"] = "--diagnosis"}
+ local flags_added = {}
+ for _, arg in ipairs(xmake._COMMAND_ARGV or {}) do
+ local flag = flags[arg]
+ if flag and not flags_added[flag] then
+ table.insert(argv, flag)
+ flags_added[flag] = true
+ end
+ end
+ table.insert(argv, "private.action.addon.impl.install_addons")
+ table.insert(argv, os.projectdir())
+ table.insert(argv, datafile)
+ local exitcode, errors = os.execv(os.programfile(), argv, {curdir = addon.workdir()})
+ os.rm(datafile)
+ if exitcode ~= 0 then
+ return {ok = false, errors = errors or "install the addons of this project failed!"}
+ end
+
+ -- we have loaded the registry and its caches before installing them, so we need to reload it
+ addon.reload()
+ project._pin_addons()
+ rule.clear()
+ task.clear()
+ return {ok = true, installed = true}
+end
+
-- load the project file
-function project._load(force, disable_filter)
+--
+-- @param opt the options
+-- - force: load the project file again even if it has been loaded
+-- - disable_filter: disable the interpreter filter, e.g. `$(plat)`
+-- - skip_addons: do not install the addons which this project declares
+-- - addons_installed: the addons have been installed, we are loading it again
+--
+function project._load(opt)
+ opt = opt or {}
+
+ -- use the locked versions of the addons which this project declares
+ project._pin_addons()
-- has already been loaded?
- if project._memcache():get("rootinfo") and not force then
+ if project._memcache():get("rootinfo") and not opt.force then
return true
end
@@ -244,6 +366,12 @@ function project._load(force, disable_filter)
-- get interpreter
local interp = project.interpreter()
+ -- this project declares the addons which it needs, e.g. add_addons("esp32-devel"),
+ -- but we can only know them after loading it, so this pass must survive the references
+ -- of the addons which are not installed yet, and we load it again after installing them,
+ -- e.g. includes("@addon/esp32-devel/board")
+ interp:includes_unresolved_set(not opt.addons_installed)
+
-- load script
local ok, errors = interp:load(project.rootfile(), {on_load_data = function (data)
for _, xmakerc_file in ipairs(project.rcfiles()) do
@@ -261,13 +389,30 @@ function project._load(force, disable_filter)
end
-- load the root info of the project
- local rootinfo, errors = project._load_scope("root", true, not disable_filter)
+ local rootinfo, errors = project._load_scope("root", true, not opt.disable_filter)
if not rootinfo then
return false, errors
end
+ -- install the addons which this project declares, and then load it again with them
+ --
+ -- @note we do not install them for the option menu, it merges the project tasks in a
+ -- best-effort way and every command builds it, @see project._load_tasks()
+ --
+ if not opt.skip_addons and not opt.addons_installed then
+ local result = project._install_addons(rootinfo)
+ if not result.ok then
+ os.cd(oldir)
+ return false, result.errors
+ end
+ if result.installed then
+ os.cd(oldir)
+ return project._load({force = true, disable_filter = opt.disable_filter, addons_installed = true})
+ end
+ end
+
-- load the root info of the target
- local rootinfo_target, errors = project._load_scope("root.target", true, not disable_filter)
+ local rootinfo_target, errors = project._load_scope("root.target", true, not opt.disable_filter)
if not rootinfo_target then
return false, errors
end
@@ -311,6 +456,11 @@ function project._load_scope(scope_kind, deduplicate, enable_filter)
end
-- load tasks
+--
+-- @note we should not install the addons which this project declares here, the option menu
+-- merges the project tasks in a best-effort way and every command builds it,
+-- e.g. `xmake lua`, `xmake addon --remove --all`, @see xmake/core/main.lua
+--
function project._load_tasks()
-- the project file is not found?
@@ -319,7 +469,7 @@ function project._load_tasks()
end
-- load the project file first and disable filter
- local ok, errors = project._load(true, true)
+ local ok, errors = project._load({force = true, disable_filter = true, skip_addons = true})
if not ok then
return nil, errors
end
@@ -400,7 +550,7 @@ function project._load_targets()
-- load all requires first and reload the project file to ensure has_package() works for targets
local requires = project.required_packages()
- local ok, errors = project._load(true)
+ local ok, errors = project._load({force = true})
if not ok then
return nil, errors
end
@@ -481,7 +631,7 @@ function project._load_options(disable_filter)
end
-- reload the project file to ensure `if is_plat() then add_packagedirs() end` works
- local ok, errors = project._load(true, disable_filter)
+ local ok, errors = project._load({force = true, disable_filter = disable_filter})
if not ok then
return nil, errors
end
@@ -655,6 +805,9 @@ function project.apis()
, "add_requires"
, "add_requireconfs"
, "add_repositories"
+ -- the addons which this project needs, they are installed automatically,
+ -- e.g. add_addons("esp32-devel 1.0.x"), @see core/project/addons.lua
+ , "add_addons"
}
, paths =
{
@@ -707,6 +860,10 @@ function project.interpreter()
-- set root scope
interp:rootscope_set("target")
+ -- the project file can reference the includes files of the addons,
+ -- e.g. includes("@addon/esp32-devel/board")
+ interp:includes_resolver_add(addon.find_includes)
+
-- define apis for rule
interp:api_define(rule.apis())
@@ -1129,7 +1286,7 @@ function project.requires_str()
if not requires_str then
-- reload the project file to handle `has_config()`
- local ok, errors = project._load(true)
+ local ok, errors = project._load({force = true})
if not ok then
os.raise(errors)
end
@@ -1259,6 +1416,10 @@ end
function project.toolchain(name, opt)
opt = opt or {}
local parseinfo = toolchain.parsename(name) -- we need to ignore `@packagename`
+ -- the addon toolchains are only loaded from the addons, e.g. set_toolchains("@addon/esp32/xtensa")
+ if parseinfo.addon_prefix then
+ return nil
+ end
local toolchain_name = parseinfo.name
local info = project._toolchains()[toolchain_name]
if info == nil and opt.namespace then
diff --git a/xmake/core/project/rule.lua b/xmake/core/project/rule.lua
index 332844ca4..5b3b3aa78 100644
--- a/xmake/core/project/rule.lua
+++ b/xmake/core/project/rule.lua
@@ -31,6 +31,7 @@ local global = require("base/global")
local interpreter = require("base/interpreter")
local instance_deps = require("base/private/instance_deps")
local select_script = require("base/private/select_script")
+local addon = require("package/addon")
local config = require("project/config")
local sandbox = require("sandbox/sandbox")
local sandbox_os = require("sandbox/modules/os")
@@ -223,6 +224,11 @@ function rule._directories()
}
end
+-- the rule directories of the installed addons, e.g. ~/.xmake/addons/<name>/<version>/rules
+function rule._addon_directories()
+ return addon.payloadinfos("rules")
+end
+
-- the interpreter
function rule._interpreter()
@@ -453,9 +459,205 @@ function rule.new(name, info, opt)
return instance
end
+-- get the file which declares the given rule, e.g. c.build -> <rulesdir>/c++/xmake.lua
+--
+-- most of the rule names match their directory name, so we only need it for the few
+-- which do not, e.g. c.build, win.sdk.resource, and we get them by scanning the rule
+-- declarations, which is much cheaper than interpreting all the rule files
+--
+function rule._rulefile(name)
+ local rulefiles = rule._RULEFILES
+ if rulefiles == nil then
+
+ -- @note we only limit the fast paths, a rule file which is nested deeper than this
+ -- is still found by the full load, @see rule.rules
+ local maxrecursion = 4
+ rulefiles = {}
+ for _, dir in ipairs(rule._directories()) do
+ for _, filepath in ipairs(os.files(path.join(dir, "**/xmake.lua"), {recursion = maxrecursion})) do
+ local content = io.readfile(filepath)
+ if content then
+ for rulename in content:gmatch("rule%s*%(%s*\"(.-)\"%s*%)") do
+ rulefiles[rulename] = filepath
+ end
+ end
+ end
+ end
+ rule._RULEFILES = rulefiles
+ end
+ return rulefiles[name]
+end
+
+-- load the rules which may provide the given rule name
+--
+-- there are hundreds of builtin rules, but a project only uses a few of them,
+-- so we do not load all of them, we only load the group which may provide it,
+-- e.g. mode.debug -> <rulesdir>/mode/**/xmake.lua
+--
+function rule._load_ondemand(name)
+
+ -- it has been loaded with another group? e.g. c.build.pcheader comes with the c++ group
+ local loaded = rule._LOADED
+ if loaded == nil then
+ loaded = {}
+ rule._LOADED = loaded
+ end
+ local instance = loaded[name]
+ if instance then
+ return instance
+ end
+
+ local groups = rule._GROUPS
+ if groups == nil then
+ groups = {}
+ rule._GROUPS = groups
+ end
+
+ -- the rules of an addon are always referenced with its name,
+ -- e.g. add_rules("@addon/esp32/flash"), so we only load this addon
+ local maxrecursion = 4
+ local groupkey, files, opt
+ if name:startswith("@addon/") then
+ local referenceinfo = addon.resolve_reference(name, "/", "rules")
+ if not referenceinfo then
+ return
+ end
+ groupkey = "@addon/" .. referenceinfo.addon
+ files = os.files(path.join(referenceinfo.dir, "**/xmake.lua"), {recursion = maxrecursion})
+ opt = {prefix = groupkey .. "/"}
+ else
+ -- the group is the first part of the rule name, and it's usually the directory
+ -- name of its rules, e.g. mode.debug -> <rulesdir>/mode
+ groupkey = name:split(".", {plain = true})[1]
+ files = {}
+ for _, dir in ipairs(rule._directories()) do
+ local groupdir = path.join(dir, groupkey)
+ table.join2(files, os.files(path.join(groupdir, "xmake.lua")))
+ table.join2(files, os.files(path.join(groupdir, "**/xmake.lua"), {recursion = maxrecursion}))
+ end
+
+ -- the rule name does not match its directory name? we can only get its file
+ -- from the rule declarations, e.g. c.build -> <rulesdir>/c++/xmake.lua
+ if #files == 0 then
+ local rulefile = rule._rulefile(name)
+ if not rulefile then
+ return
+ end
+ groupkey = rulefile
+ files = {rulefile}
+ end
+ end
+
+ if not groups[groupkey] then
+ local ruleinfos = {}
+ rule._load_rulefiles(ruleinfos, files, opt)
+ for rulename, ruleinfo in pairs(ruleinfos) do
+ loaded[rulename] = rule.new(rulename, ruleinfo)
+ end
+ groups[groupkey] = true
+ end
+ return loaded[name]
+end
+
+-- report the missing addon of the given rule reference, e.g. add_rules("@addon/esp32/flash")
+--
+-- it's either not installed at all, or it's installed but does not provide this rule
+--
+function rule._raise_addon_notfound(name)
+ local referenceinfo, errors = addon.resolve_reference(name, "/", "rules")
+ if errors then
+ os.raise(errors)
+ end
+ os.raise("rule(%s) not found!\nplease install the addon which provides it first: xmake addon --install %s",
+ name, referenceinfo and referenceinfo.addon or "<addon>")
+end
+
+-- clear the loaded rules, e.g. some addons may be installed just now
+function rule.clear()
+ rule._RULES = nil
+ rule._GROUPS = nil
+ rule._LOADED = nil
+ rule._RULEFILES = nil
+end
+
-- get the given global rule
+--
+-- @param name the rule name, the rules of the installed addons need the
+-- `@addon/<addon>/` prefix, e.g. "@addon/esp32/flash"
+--
function rule.rule(name)
- return rule.rules()[name]
+ local instance
+ if rule._RULES then
+ -- all the rules have been loaded, e.g. rule.rules()
+ instance = rule._RULES[name]
+ else
+ -- we only load the rules which may provide it
+ instance = rule._load_ondemand(name)
+ if instance == nil then
+ -- @note the rule name may not match its directory name, so we need
+ -- to load all the rules to be sure that it does not exist
+ instance = rule.rules()[name]
+ end
+ end
+ if instance == nil and name:startswith("@addon/") then
+ rule._raise_addon_notfound(name)
+ end
+ return instance
+end
+
+-- load the rules from the given directory
+function rule._load_rules(ruleinfos, dir, opt)
+ -- @note we may load a group directory directly, e.g. <rulesdir>/mode/xmake.lua
+ rule._load_rulefiles(ruleinfos, table.join(os.files(path.join(dir, "xmake.lua")),
+ os.files(path.join(dir, "**/xmake.lua"))), opt)
+end
+
+-- load the rules from the given files
+function rule._load_rulefiles(ruleinfos, files, opt)
+ opt = opt or {}
+ if files then
+ for _, filepath in ipairs(files) do
+ local results, errors = rule._load(filepath)
+ if results then
+ for rulename, ruleinfo in pairs(results) do
+ -- the addon rules are always referenced with the addon name,
+ -- e.g. add_rules("@addon/esp32/flash")
+ local fullname = (opt.prefix or "") .. rulename
+ if opt.prefix and ruleinfos[fullname] == nil then
+ -- the addon rules can depend on the other rules of the same addon,
+ -- e.g. add_deps("@self/base") -> add_deps("@addon/<addon>/base")
+ rule._replace_selfdeps(ruleinfo, opt.prefix)
+ end
+ ruleinfos[fullname] = ruleinfo
+ end
+ else
+ os.raise(errors)
+ end
+ end
+ end
+end
+
+-- replace the `@self/` dependencies of the addon rules with the full names
+function rule._replace_selfdeps(ruleinfo, prefix)
+ local deps = {}
+ local replace = function (depname)
+ if depname:startswith("@self/") then
+ return prefix .. depname:sub(#"@self/" + 1)
+ end
+ return depname
+ end
+ for _, depname in ipairs(table.wrap(ruleinfo:get("deps"))) do
+ table.insert(deps, replace(depname))
+ end
+ if #deps > 0 then
+ ruleinfo:set("deps", table.unwrap(deps))
+ end
+ for depname, extraconf in pairs(table.wrap(ruleinfo:extraconf("deps"))) do
+ local newname = replace(depname)
+ if newname ~= depname then
+ ruleinfo:extraconf_set("deps", newname, extraconf)
+ end
+ end
end
-- get global rules
@@ -463,26 +665,21 @@ function rule.rules()
local rules = rule._RULES
if rules == nil then
local ruleinfos = {}
- local dirs = rule._directories()
- for _, dir in ipairs(dirs) do
- local files = os.files(path.join(dir, "**/xmake.lua"))
- if files then
- for _, filepath in ipairs(files) do
- local results, errors = rule._load(filepath)
- if results then
- table.join2(ruleinfos, results)
- else
- os.raise(errors)
- end
- end
- end
+ for _, dir in ipairs(rule._directories()) do
+ rule._load_rules(ruleinfos, dir)
+ end
+ for _, addoninfo in ipairs(rule._addon_directories()) do
+ rule._load_rules(ruleinfos, addoninfo.dir, {prefix = "@addon/" .. addoninfo.name .. "/"})
end
-- make rule instances
+ --
+ -- @note we reuse the rules which have been loaded on demand,
+ -- otherwise we would have two instances of the same rule
+ local loaded = rule._LOADED or {}
rules = {}
for rulename, ruleinfo in pairs(ruleinfos) do
- local instance = rule.new(rulename, ruleinfo)
- rules[rulename] = instance
+ rules[rulename] = loaded[rulename] or rule.new(rulename, ruleinfo)
end
rule._RULES = rules
end
diff --git a/xmake/core/project/target.lua b/xmake/core/project/target.lua
index cc5df14d4..e51ffeb8b 100644
--- a/xmake/core/project/target.lua
+++ b/xmake/core/project/target.lua
@@ -84,7 +84,7 @@ function _instance:_load_rule(ruleinst, suffix)
if cache[key] == nil then
local on_load = ruleinst:script("load" .. (suffix and ("_" .. suffix) or ""))
if on_load then
- local ok, errors = sandbox.load(on_load, self)
+ local ok, errors = sandbox.call(on_load, self)
cache[key] = {ok, errors}
else
cache[key] = {true}
@@ -129,7 +129,7 @@ function _instance:_load()
-- do load for target
local on_load = self:script("load")
if on_load then
- ok, errors = sandbox.load(on_load, self)
+ ok, errors = sandbox.call(on_load, self)
if not ok then
return false, errors
end
@@ -159,7 +159,7 @@ function _instance:_load_after()
-- do load for target
local after_load = self:script("load_after")
if after_load then
- local ok, errors = sandbox.load(after_load, self)
+ local ok, errors = sandbox.call(after_load, self)
if not ok then
return false, errors
end
@@ -3296,6 +3296,14 @@ function target.linkname(filename, opt)
linkname, count = filename:gsub(target.filename("__pattern__", "static", {plat = "windows"}):gsub("%.", "%%."):gsub("__pattern__", "(.+)") .. "$", "%1")
end
if count > 0 and linkname then
+ -- the library name itself may end with `.lib`, e.g. vcpkg installs `Luau.CLI.lib.lib`,
+ -- we cannot strip it, nf_link() passes the names ending with `.lib` to the linker
+ -- as-is, and it would look for `Luau.CLI.lib`
+ --
+ -- @see https://github.com/xmake-io/xmake/issues/7708
+ if opt.plat == "windows" and linkname:endswith(".lib") then
+ return filename
+ end
return linkname
end
-- fallback to the generic unix library name, libxxx.a, libxxx.so, ..
diff --git a/xmake/core/sandbox/modules/import/core/base/scheduler.lua b/xmake/core/sandbox/modules/import/core/base/scheduler.lua
index 222bee391..a3f9741c3 100644
--- a/xmake/core/sandbox/modules/import/core/base/scheduler.lua
+++ b/xmake/core/sandbox/modules/import/core/base/scheduler.lua
@@ -96,7 +96,11 @@ end
-- resume the given coroutine
function sandbox_core_base_scheduler.co_resume(co, ...)
- return scheduler:resume(co:thread(), ...)
+ local ok, errors = scheduler:co_resume(co, ...)
+ if not ok then
+ raise(errors)
+ end
+ return ok, errors
end
-- suspend the current coroutine
diff --git a/xmake/core/sandbox/modules/import/core/base/semver.lua b/xmake/core/sandbox/modules/import/core/base/semver.lua
index 45ba8b74f..04df315eb 100644
--- a/xmake/core/sandbox/modules/import/core/base/semver.lua
+++ b/xmake/core/sandbox/modules/import/core/base/semver.lua
@@ -50,15 +50,10 @@ function sandbox_core_base_semver.match(str, pos, pattern)
end
-- is valid version?
-function sandbox_core_base_semver.is_valid(version)
- return semver.parse(version) ~= nil
-end
+sandbox_core_base_semver.is_valid = semver.is_valid
-- is valid version range?
-function sandbox_core_base_semver.is_valid_range(range)
- local ok = semver.satisfies("1.0", range)
- return ok ~= nil
-end
+sandbox_core_base_semver.is_valid_range = semver.is_valid_range
-- compare two version strings
--
diff --git a/xmake/core/sandbox/modules/import/core/package/addon.lua b/xmake/core/sandbox/modules/import/core/package/addon.lua
new file mode 100644
index 000000000..9f080cc93
--- /dev/null
+++ b/xmake/core/sandbox/modules/import/core/package/addon.lua
@@ -0,0 +1,96 @@
+--!A cross-platform build utility based on Lua
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+-- Copyright (C) 2015-present, Xmake Open Source Community.
+--
+-- @author ruki
+-- @file addon.lua
+--
+
+-- define module
+local sandbox_core_package_addon = sandbox_core_package_addon or {}
+
+-- load modules
+local addon = require("package/addon")
+local raise = require("sandbox/modules/raise")
+
+-- inherit some builtin interfaces
+sandbox_core_package_addon.installdir = addon.installdir
+sandbox_core_package_addon.workdir = addon.workdir
+sandbox_core_package_addon.dirname = addon.dirname
+sandbox_core_package_addon.owner = addon.owner
+sandbox_core_package_addon.payloadinfos = addon.payloadinfos
+sandbox_core_package_addon.payloads_of = addon.payloads_of
+sandbox_core_package_addon.payloadroot = addon.payloadroot
+sandbox_core_package_addon.addons = addon.addons
+sandbox_core_package_addon.versions = addon.versions
+sandbox_core_package_addon.unregister = addon.unregister
+
+-- get the manifest of the given addon directory, e.g. <sourcedir>/addon.lua
+--
+-- @param sourcedir the addon source or install directory
+-- @return the manifest, it will be nil if this addon does not describe itself
+--
+function sandbox_core_package_addon.manifest(sourcedir)
+ local manifest, errors = addon.manifest(sourcedir)
+ if errors then
+ raise(errors)
+ end
+ return manifest
+end
+
+-- resolve the given addon reference, e.g. `@addon/<addon>/<name>`, `@self/<name>`
+--
+-- @param reference the addon reference
+-- @param sep the separator, e.g. "/", "."
+-- @param kind the payload kind, e.g. "rules", "modules"
+-- @param opt the options, e.g. {scriptdir = "..."}
+--
+-- @return the reference information, e.g. {dir = "...", name = "flash", addon = "esp32"}
+--
+function sandbox_core_package_addon.resolve_reference(reference, sep, kind, opt)
+ local referenceinfo, errors = addon.resolve_reference(reference, sep, kind, opt)
+ if errors then
+ raise(errors)
+ end
+ return referenceinfo
+end
+
+-- register the given installed addon
+--
+-- @param name the addon name
+-- @param version the addon version, e.g. "1.0.1", "latest"
+-- @param opt the options, e.g. {description = "...", deps = {"foo"}}
+--
+function sandbox_core_package_addon.register(name, version, opt)
+ local ok, errors = addon.register(name, version, opt)
+ if not ok then
+ raise(errors)
+ end
+end
+
+-- remove the given installed addon
+--
+-- @param name the addon name
+-- @param opt the options, e.g. {force = true}
+--
+function sandbox_core_package_addon.remove(name, opt)
+ local ok, errors = addon.remove(name, opt)
+ if not ok then
+ raise(errors)
+ end
+end
+
+-- return module
+return sandbox_core_package_addon
diff --git a/xmake/core/sandbox/modules/import/core/project/addons.lua b/xmake/core/sandbox/modules/import/core/project/addons.lua
new file mode 100644
index 000000000..ff2fe14c0
--- /dev/null
+++ b/xmake/core/sandbox/modules/import/core/project/addons.lua
@@ -0,0 +1,35 @@
+--!A cross-platform build utility based on Lua
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+-- Copyright (C) 2015-present, Xmake Open Source Community.
+--
+-- @author ruki
+-- @file addons.lua
+--
+
+-- define module
+local sandbox_core_project_addons = sandbox_core_project_addons or {}
+
+-- load modules
+local addons = require("project/addons")
+
+-- inherit some builtin interfaces
+sandbox_core_project_addons.lockfile = addons.lockfile
+sandbox_core_project_addons.lockfile_version = addons.lockfile_version
+sandbox_core_project_addons.locked = addons.locked
+sandbox_core_project_addons.locked_valid = addons.locked_valid
+sandbox_core_project_addons.requirename = addons.requirename
+
+-- return module
+return sandbox_core_project_addons
diff --git a/xmake/core/sandbox/modules/import/core/project/project.lua b/xmake/core/sandbox/modules/import/core/project/project.lua
index 9b69349d0..4964d988c 100644
--- a/xmake/core/sandbox/modules/import/core/project/project.lua
+++ b/xmake/core/sandbox/modules/import/core/project/project.lua
@@ -161,6 +161,10 @@ end]]
function sandbox_core_project._load_package_rules_for_target(target)
for _, rulename in ipairs(table.wrap(target:get("rules"))) do
local packagename = rulename:match("@(.-)/")
+ -- @note we need to ignore the addon rules, e.g. add_rules("@addon/foo")
+ if packagename == "addon" then
+ packagename = nil
+ end
if packagename then
local ruleinst
local pkginfo = project.required_package(packagename)
diff --git a/xmake/core/sandbox/modules/import/core/sandbox/module.lua b/xmake/core/sandbox/modules/import/core/sandbox/module.lua
index 2090c1f52..294cf7556 100644
--- a/xmake/core/sandbox/modules/import/core/sandbox/module.lua
+++ b/xmake/core/sandbox/modules/import/core/sandbox/module.lua
@@ -30,6 +30,7 @@ local table = require("base/table")
local string = require("base/string")
local option = require("base/option")
local global = require("base/global")
+local addon = require("package/addon")
local config = require("project/config")
local memcache = require("cache/memcache")
local sandbox = require("sandbox/sandbox")
@@ -386,7 +387,7 @@ function core_sandbox_module._find_and_load(name, opt)
errors = moduleinfo[2]
else
module, errors = core_sandbox_module._load(moduledir, name, {
- instance = idx < #modules_directories and opt.instance or nil, -- last modules need not fork sandbox
+ instance = moduledir ~= core_sandbox_module.coredir() and opt.instance or nil, -- the core modules need not fork sandbox
module = module,
always_build = always_build,
modulekind = modulekind})
@@ -419,9 +420,10 @@ end
function core_sandbox_module.directories()
local moduledirs = memcache.get("core_sandbox_module", "moduledirs")
if not moduledirs then
+ -- @note the core modules directory must be the last one, @see core_sandbox_module._find_and_load
moduledirs = { path.join(global.directory(), "modules"),
path.join(os.programdir(), "modules"),
- path.join(os.programdir(), "core/sandbox/modules/import")}
+ core_sandbox_module.coredir()}
local modulesdir = os.getenv("XMAKE_MODULES_DIR")
if modulesdir and os.isdir(modulesdir) then
table.insert(moduledirs, 1, modulesdir)
@@ -431,6 +433,14 @@ function core_sandbox_module.directories()
return moduledirs
end
+-- get the core modules directory
+--
+-- @note the modules in this directory are loaded without sandbox, because they need `require`
+--
+function core_sandbox_module.coredir()
+ return path.join(os.programdir(), "core/sandbox/modules/import")
+end
+
-- add module directories
function core_sandbox_module.add_directories(...)
local moduledirs = core_sandbox_module.directories()
@@ -442,6 +452,14 @@ end
-- find module
function core_sandbox_module.find(name)
+
+ -- an addon can export some modules as the global modules, they are also visible here,
+ -- e.g. find_toolname() looks for `detect.tools.find_xxx` with it, @see addon.globalmodules()
+ local globalmodulesdir = addon.globalmodules()[name]
+ if globalmodulesdir and core_sandbox_module._find(globalmodulesdir, name) then
+ return true
+ end
+
for _, moduledir in ipairs(core_sandbox_module.directories()) do
if (core_sandbox_module._find(moduledir, name)) then
return true
@@ -498,6 +516,24 @@ function core_sandbox_module.import(name, opt)
local scope_parent = getfenv(2)
assert(scope_parent)
+ -- import the modules of the installed addons? e.g. import("@addon.foo")
+ --
+ -- @note we need the `@addon.` prefix to distinguish them from the builtin modules
+ --
+ -- import the modules of an addon?
+ -- e.g. import("@addon.esp32.sdkconfig"), import("@self.sdkconfig")
+ local addon_modulesdir
+ local addon_reference = name
+ if addon.is_reference(name, ".") then
+ local referenceinfo, errors = addon.resolve_reference(name, ".", "modules",
+ {scriptdir = opt.scriptdir or sandbox.instance() and sandbox.instance():rootdir()})
+ if not referenceinfo then
+ raise(errors)
+ end
+ addon_modulesdir = referenceinfo.dir
+ name = referenceinfo.name
+ end
+
-- get module name
local modulename = core_sandbox_module.name(name)
if not modulename then
@@ -515,7 +551,25 @@ function core_sandbox_module.import(name, opt)
local rootdir = opt.rootdir or instance:rootdir()
-- init module directories (disable local packages?)
- local modules_directories = (opt.nolocal or not rootdir) and core_sandbox_module.directories() or table.join(rootdir, core_sandbox_module.directories())
+ local modules_directories
+ if addon_modulesdir then
+ -- the addon modules are always resolved from the addon `modules` directory only,
+ -- e.g. import("@addon.esp32.sdkconfig"), import("@self.sdkconfig")
+ modules_directories = {addon_modulesdir}
+ else
+ modules_directories = (opt.nolocal or not rootdir) and core_sandbox_module.directories() or table.join(rootdir, core_sandbox_module.directories())
+
+ -- an addon can export some modules as the global modules, e.g. add_globalmodules("core.tools.esptool"),
+ -- so that the internal calls can import them with their plain names, e.g. import("core.tools.esptool")
+ --
+ -- @note we only accept the declared names, the other modules of this addon are
+ -- still private and can only be imported with `@addon.`/`@self.`
+ --
+ local globalmodulesdir = addon.globalmodules()[name]
+ if globalmodulesdir then
+ table.insert(modules_directories, rootdir and 2 or 1, globalmodulesdir)
+ end
+ end
-- load module
local loadopt = table.clone(opt) or {}
@@ -554,6 +608,8 @@ function core_sandbox_module.import(name, opt)
if not found then
if opt.try then
return nil
+ elseif addon_modulesdir then
+ raise("cannot import module: %s, not found!", addon_reference)
else
raise("cannot import module: %s, not found!", name)
end
diff --git a/xmake/core/sandbox/modules/import/core/sandbox/sandbox.lua b/xmake/core/sandbox/modules/import/core/sandbox/sandbox.lua
index 0a9cae170..7303e901e 100644
--- a/xmake/core/sandbox/modules/import/core/sandbox/sandbox.lua
+++ b/xmake/core/sandbox/modules/import/core/sandbox/sandbox.lua
@@ -134,6 +134,19 @@ function sandbox_core_sandbox.filter(script)
return instance:filter()
end
+-- fork a new sandbox and bind a raw script
+function sandbox_core_sandbox.fork(script)
+ local instance = sandbox.instance()
+ if not instance then
+ raise("cannot get sandbox instance!")
+ end
+ local newinst, errors = instance:fork(script)
+ if not newinst then
+ raise(errors)
+ end
+ return newinst
+end
+
-- get all builtin modules
function sandbox_core_sandbox.builtin_modules()
return sandbox.builtin_modules()
diff --git a/xmake/core/sandbox/modules/import/core/tool/compiler.lua b/xmake/core/sandbox/modules/import/core/tool/compiler.lua
index 71de70d2a..28a3d4d92 100644
--- a/xmake/core/sandbox/modules/import/core/tool/compiler.lua
+++ b/xmake/core/sandbox/modules/import/core/tool/compiler.lua
@@ -207,7 +207,7 @@ function sandbox_core_tool_compiler.features(langkind, opt)
local flags = instance:compflags(opt)
-- get features
- local ok, results_or_errors = sandbox.load(sandbox_core_tool_compiler._features, instance:name(), {flags = flags, program = instance:program(), envs = instance:runenvs()})
+ local ok, results_or_errors = sandbox.call(sandbox_core_tool_compiler._features, instance:name(), {flags = flags, program = instance:program(), envs = instance:runenvs()})
if not ok then
raise(results_or_errors)
end
@@ -259,7 +259,7 @@ function sandbox_core_tool_compiler.has_features(features, opt)
for sourcekind, features in pairs(features_by_kind) do
local instance = sandbox_core_tool_compiler.load(sourcekind, opt)
local flags = instance:compflags(opt)
- local ok, results_or_errors = sandbox.load(sandbox_core_tool_compiler._has_features, instance:name(), features, {flags = flags, program = instance:program(), envs = instance:runenvs()})
+ local ok, results_or_errors = sandbox.call(sandbox_core_tool_compiler._has_features, instance:name(), features, {flags = flags, program = instance:program(), envs = instance:runenvs()})
if not ok then
raise(results_or_errors)
end
diff --git a/xmake/core/sandbox/modules/import/lib/detect/find_directory.lua b/xmake/core/sandbox/modules/import/lib/detect/find_directory.lua
index 63392a751..ad48cff73 100644
--- a/xmake/core/sandbox/modules/import/lib/detect/find_directory.lua
+++ b/xmake/core/sandbox/modules/import/lib/detect/find_directory.lua
@@ -36,7 +36,7 @@ function sandbox_lib_detect_find_directory._expand_paths(paths)
for _, _path in ipairs(table.wrap(paths)) do
local _path = _path
if type(_path) == "function" then
- local ok, result_or_errors = sandbox.load(_path)
+ local ok, result_or_errors = sandbox.call(_path)
if ok then
_path = result_or_errors or ""
else
diff --git a/xmake/core/sandbox/modules/import/lib/detect/find_file.lua b/xmake/core/sandbox/modules/import/lib/detect/find_file.lua
index e642f45b7..67db6c443 100644
--- a/xmake/core/sandbox/modules/import/lib/detect/find_file.lua
+++ b/xmake/core/sandbox/modules/import/lib/detect/find_file.lua
@@ -37,7 +37,7 @@ function sandbox_lib_detect_find_file._expand_paths(paths)
for _, _path in ipairs(table.wrap(paths)) do
local _path = _path
if type(_path) == "function" then
- local ok, result_or_errors = sandbox.load(_path)
+ local ok, result_or_errors = sandbox.call(_path)
if ok then
_path = result_or_errors or ""
else
diff --git a/xmake/core/sandbox/modules/import/lib/detect/find_path.lua b/xmake/core/sandbox/modules/import/lib/detect/find_path.lua
index 159d7dbdc..6dd775752 100644
--- a/xmake/core/sandbox/modules/import/lib/detect/find_path.lua
+++ b/xmake/core/sandbox/modules/import/lib/detect/find_path.lua
@@ -36,7 +36,7 @@ function sandbox_lib_detect_find_path._expand_paths(paths)
for _, _path in ipairs(table.wrap(paths)) do
local _path = _path
if type(_path) == "function" then
- local ok, result_or_errors = sandbox.load(_path)
+ local ok, result_or_errors = sandbox.call(_path)
if ok then
_path = result_or_errors or ""
else
diff --git a/xmake/core/sandbox/modules/import/lib/detect/find_program.lua b/xmake/core/sandbox/modules/import/lib/detect/find_program.lua
index 0ca3f8d19..eee1d13b6 100644
--- a/xmake/core/sandbox/modules/import/lib/detect/find_program.lua
+++ b/xmake/core/sandbox/modules/import/lib/detect/find_program.lua
@@ -64,7 +64,7 @@ function sandbox_lib_detect_find_program._do_check(program, opt)
elseif type(opt.check) == "table" then
ok, errors = os.runv(program, opt.check, {envs = opt.envs, shell = opt.shell})
else
- ok, errors = sandbox.load(opt.check, program)
+ ok, errors = sandbox.call(opt.check, program)
end
-- check failed? print verbose error info
@@ -112,7 +112,7 @@ function sandbox_lib_detect_find_program._find_from_paths(name, paths, opt)
-- format path for builtin variables
if type(_path) == "function" then
- local ok, results = sandbox.load(_path)
+ local ok, results = sandbox.call(_path)
if ok then
_path = results or ""
else
diff --git a/xmake/core/sandbox/modules/import/lib/detect/find_programver.lua b/xmake/core/sandbox/modules/import/lib/detect/find_programver.lua
index 0d76526e5..2a8629855 100644
--- a/xmake/core/sandbox/modules/import/lib/detect/find_programver.lua
+++ b/xmake/core/sandbox/modules/import/lib/detect/find_programver.lua
@@ -79,7 +79,7 @@ function sandbox_lib_detect_find_programver.main(program, opt)
local outdata = nil
local command = opt.command
if type(command) == "function" then
- ok, outdata = sandbox.load(command)
+ ok, outdata = sandbox.call(command)
if not ok and outdata and option.get("diagnosis") then
utils.cprint("${color.warning}checkinfo: ${clear dim}" .. outdata)
end
@@ -94,7 +94,7 @@ function sandbox_lib_detect_find_programver.main(program, opt)
if ok and outdata and #outdata > 0 then
local parse = opt.parse
if type(parse) == "function" then
- ok, result = sandbox.load(parse, outdata)
+ ok, result = sandbox.call(parse, outdata)
if not ok and result and option.get("diagnosis") then
utils.cprint("${color.warning}checkinfo: ${clear dim}" .. result)
result = nil
diff --git a/xmake/core/sandbox/modules/import/lib/lua/error.lua b/xmake/core/sandbox/modules/import/lib/lua/error.lua
new file mode 100644
index 000000000..4f38f1647
--- /dev/null
+++ b/xmake/core/sandbox/modules/import/lib/lua/error.lua
@@ -0,0 +1,36 @@
+--!A cross-platform build utility based on Lua
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+-- Copyright (C) 2015-present, Xmake Open Source Community.
+--
+-- @author ruki
+-- @file error.lua
+--
+
+-- the native lua `error`
+--
+-- it is not in the sandbox by default, `raise` is the one to use: it formats the
+-- message and it is what `try`/`catch` understands. `error` is the plain one, for the
+-- code which wants to throw a value rather than a message
+--
+-- @note the same in lua 5.1 and 5.4
+--
+-- e.g.
+--
+-- import("lib.lua.error")
+--
+-- error("something went wrong")
+-- error({code = 1}) -- a value, not a message
+--
+return error
diff --git a/xmake/core/sandbox/modules/import/lib/lua/getmetatable.lua b/xmake/core/sandbox/modules/import/lib/lua/getmetatable.lua
new file mode 100644
index 000000000..fcef76eb3
--- /dev/null
+++ b/xmake/core/sandbox/modules/import/lib/lua/getmetatable.lua
@@ -0,0 +1,32 @@
+--!A cross-platform build utility based on Lua
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+-- Copyright (C) 2015-present, Xmake Open Source Community.
+--
+-- @author ruki
+-- @file getmetatable.lua
+--
+
+-- the native lua `getmetatable`
+--
+-- @note the same in lua 5.1 and 5.4, it returns the `__metatable` field when the
+-- metatable is protected
+--
+-- e.g.
+--
+-- import("lib.lua.getmetatable")
+--
+-- local mt = getmetatable(obj)
+--
+return getmetatable
diff --git a/xmake/core/sandbox/modules/import/lib/lua/next.lua b/xmake/core/sandbox/modules/import/lib/lua/next.lua
new file mode 100644
index 000000000..65313dd1a
--- /dev/null
+++ b/xmake/core/sandbox/modules/import/lib/lua/next.lua
@@ -0,0 +1,36 @@
+--!A cross-platform build utility based on Lua
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+-- Copyright (C) 2015-present, Xmake Open Source Community.
+--
+-- @author ruki
+-- @file next.lua
+--
+
+-- the native lua `next`
+--
+-- it is not in the sandbox by default, `pairs` covers the iteration of a table. `next`
+-- is what is left for the two things `pairs` cannot say: whether a table is empty, and
+-- stepping a traversal by hand
+--
+-- @note the same in lua 5.1 and 5.4
+--
+-- e.g.
+--
+-- import("lib.lua.next")
+--
+-- if next(tbl) == nil then ... end -- the table is empty
+-- local key, value = next(tbl) -- the first entry, in no particular order
+--
+return next
diff --git a/xmake/core/sandbox/modules/import/lib/lua/pcall.lua b/xmake/core/sandbox/modules/import/lib/lua/pcall.lua
new file mode 100644
index 000000000..1ad150943
--- /dev/null
+++ b/xmake/core/sandbox/modules/import/lib/lua/pcall.lua
@@ -0,0 +1,36 @@
+--!A cross-platform build utility based on Lua
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+-- Copyright (C) 2015-present, Xmake Open Source Community.
+--
+-- @author ruki
+-- @file pcall.lua
+--
+
+-- the native lua `pcall`
+--
+-- it is not in the sandbox by default, `try`/`catch` is the idiomatic way to handle the
+-- errors of a script, and it keeps the traceback and the error message which xmake
+-- builds. `pcall` is the plain one, for the code which only needs a boolean
+--
+-- @note the same in lua 5.1 and 5.4. `xpcall` is not exported, its message handler
+-- takes the extra arguments only since 5.2
+--
+-- e.g.
+--
+-- import("lib.lua.pcall")
+--
+-- local ok, result = pcall(function () return 1 end)
+--
+return pcall
diff --git a/xmake/core/sandbox/modules/import/lib/lua/rawequal.lua b/xmake/core/sandbox/modules/import/lib/lua/rawequal.lua
new file mode 100644
index 000000000..b9f9ea683
--- /dev/null
+++ b/xmake/core/sandbox/modules/import/lib/lua/rawequal.lua
@@ -0,0 +1,33 @@
+--!A cross-platform build utility based on Lua
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+-- Copyright (C) 2015-present, Xmake Open Source Community.
+--
+-- @author ruki
+-- @file rawequal.lua
+--
+
+-- the native lua `rawequal`
+--
+-- it compares two values without going through the `__eq` metamethod
+--
+-- @note the same in lua 5.1 and 5.4
+--
+-- e.g.
+--
+-- import("lib.lua.rawequal")
+--
+-- if rawequal(a, b) then ... end
+--
+return rawequal
diff --git a/xmake/core/sandbox/modules/import/lib/lua/rawget.lua b/xmake/core/sandbox/modules/import/lib/lua/rawget.lua
new file mode 100644
index 000000000..fe5115001
--- /dev/null
+++ b/xmake/core/sandbox/modules/import/lib/lua/rawget.lua
@@ -0,0 +1,34 @@
+--!A cross-platform build utility based on Lua
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+-- Copyright (C) 2015-present, Xmake Open Source Community.
+--
+-- @author ruki
+-- @file rawget.lua
+--
+
+-- the native lua `rawget`
+--
+-- it reads a field of a table without going through its `__index` metamethod, so it
+-- sees what the table itself holds and not what its metatable would answer
+--
+-- @note the same in lua 5.1 and 5.4
+--
+-- e.g.
+--
+-- import("lib.lua.rawget")
+--
+-- local value = rawget(tbl, "key")
+--
+return rawget
diff --git a/xmake/core/sandbox/modules/import/lib/lua/rawset.lua b/xmake/core/sandbox/modules/import/lib/lua/rawset.lua
new file mode 100644
index 000000000..917bc3d86
--- /dev/null
+++ b/xmake/core/sandbox/modules/import/lib/lua/rawset.lua
@@ -0,0 +1,33 @@
+--!A cross-platform build utility based on Lua
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+-- Copyright (C) 2015-present, Xmake Open Source Community.
+--
+-- @author ruki
+-- @file rawset.lua
+--
+
+-- the native lua `rawset`
+--
+-- it writes a field of a table without going through its `__newindex` metamethod
+--
+-- @note the same in lua 5.1 and 5.4
+--
+-- e.g.
+--
+-- import("lib.lua.rawset")
+--
+-- rawset(tbl, "key", value)
+--
+return rawset
diff --git a/xmake/core/sandbox/modules/import/lib/lua/select.lua b/xmake/core/sandbox/modules/import/lib/lua/select.lua
new file mode 100644
index 000000000..45fb1bbc9
--- /dev/null
+++ b/xmake/core/sandbox/modules/import/lib/lua/select.lua
@@ -0,0 +1,36 @@
+--!A cross-platform build utility based on Lua
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+-- Copyright (C) 2015-present, Xmake Open Source Community.
+--
+-- @author ruki
+-- @file select.lua
+--
+
+-- the native lua `select`
+--
+-- it is not in the sandbox by default, the scripts of a project rarely need to look at
+-- a raw argument list, but it is the only way to count the arguments of a vararg
+-- function without losing the nils in it
+--
+-- @note the same in lua 5.1 and 5.4
+--
+-- e.g.
+--
+-- import("lib.lua.select")
+--
+-- local count = select("#", ...) -- how many arguments, nils included
+-- local second = select(2, ...) -- everything from the second one on
+--
+return select
diff --git a/xmake/core/sandbox/modules/import/lib/lua/setmetatable.lua b/xmake/core/sandbox/modules/import/lib/lua/setmetatable.lua
new file mode 100644
index 000000000..6f1d4c330
--- /dev/null
+++ b/xmake/core/sandbox/modules/import/lib/lua/setmetatable.lua
@@ -0,0 +1,36 @@
+--!A cross-platform build utility based on Lua
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+-- Copyright (C) 2015-present, Xmake Open Source Community.
+--
+-- @author ruki
+-- @file setmetatable.lua
+--
+
+-- the native lua `setmetatable`
+--
+-- it is not in the sandbox by default: a module which builds objects with metatables is
+-- a module which is hard to serialize and to cache, and xmake passes plain tables
+-- around on purpose. it is here for the code which really wants operator overloading or
+-- an `__index` fallback
+--
+-- @note the same in lua 5.1 and 5.4
+--
+-- e.g.
+--
+-- import("lib.lua.setmetatable")
+--
+-- local obj = setmetatable({}, {__index = function (tbl, key) return "?" end})
+--
+return setmetatable
diff --git a/xmake/core/sandbox/sandbox.lua b/xmake/core/sandbox/sandbox.lua
index 758a48030..13e598b73 100644
--- a/xmake/core/sandbox/sandbox.lua
+++ b/xmake/core/sandbox/sandbox.lua
@@ -105,43 +105,13 @@ function sandbox._new()
instance:_api_register_builtin(module_name, module)
end
- -- bind instance to the public script envirnoment
- instance:bind(instance._PUBLIC)
+ -- bind the public script envirnoment
+ instance:_bindenv(instance._PUBLIC)
return instance
end
--- new a sandbox instance with the given script
-function sandbox.new(script, opt)
- opt = opt or {}
-
- -- new instance
- local self = sandbox._new()
- assert(self and self._PUBLIC and self._PRIVATE)
-
- self._PRIVATE._FILTER = opt.filter
- self._PRIVATE._ROOTDIR = opt.rootdir
- self._PRIVATE._NAMESPACE = opt.namespace
-
- -- invalid script?
- if type(script) ~= "function" then
- return nil, "invalid script!"
- end
-
- -- bind public scope
- setfenv(script, self._PUBLIC)
-
- -- save script
- self._PRIVATE._SCRIPT = script
- return self
-end
-
--- load script in the sandbox
-function sandbox.load(script, ...)
- return utils.trycall(script, sandbox._traceback, ...)
-end
-
--- bind self instance to the given script or envirnoment
-function sandbox:bind(script_or_env)
+-- bind the given script or envirnoment and the self instance
+function sandbox:_bindenv(script_or_env)
-- get envirnoment
local env = script_or_env
@@ -150,20 +120,18 @@ function sandbox:bind(script_or_env)
end
-- bind instance to the script envirnoment
- setmetatable(env, { __index = function (tbl, key)
- if type(key) == "string" and key == "_SANDBOX" and rawget(tbl, "_SANDBOX_READABLE") then
- return self
- end
- return rawget(tbl, key)
+ setmetatable(env, { __index = function (tbl, key)
+ if type(key) == "string" and key == "_SANDBOX" and rawget(tbl, "_SANDBOX_READABLE") then
+ return self
end
- , __newindex = function (tbl, key, val)
- if type(key) == "string" and (key == "_SANDBOX" or key == "_SANDBOX_READABLE") then
- return
- end
- rawset(tbl, key, val)
- end})
-
- -- ok
+ return rawget(tbl, key)
+ end,
+ __newindex = function (tbl, key, val)
+ if type(key) == "string" and (key == "_SANDBOX" or key == "_SANDBOX_READABLE") then
+ return
+ end
+ rawset(tbl, key, val)
+ end})
return script_or_env
end
@@ -205,7 +173,7 @@ function sandbox:module()
table.copy2(scope_backup, scope_public)
-- load module with sandbox
- local ok, errors = sandbox.load(self:script())
+ local ok, errors = sandbox.call(self:script())
if not ok then
return nil, errors
end
@@ -250,6 +218,36 @@ function sandbox:api_register_builtin(name, func)
sandbox._api_register_builtin(self, name, func)
end
+-- new a sandbox instance with the given script
+function sandbox.new(script, opt)
+ opt = opt or {}
+
+ -- new instance
+ local self = sandbox._new()
+ assert(self and self._PUBLIC and self._PRIVATE)
+
+ self._PRIVATE._FILTER = opt.filter
+ self._PRIVATE._ROOTDIR = opt.rootdir
+ self._PRIVATE._NAMESPACE = opt.namespace
+
+ -- invalid script?
+ if type(script) ~= "function" then
+ return nil, "invalid script!"
+ end
+
+ -- bind public scope
+ setfenv(script, self._PUBLIC)
+
+ -- save script
+ self._PRIVATE._SCRIPT = script
+ return self
+end
+
+-- call script in the sandbox
+function sandbox.call(script, ...)
+ return utils.trycall(script, sandbox._traceback, ...)
+end
+
-- get current instance in the sandbox modules
function sandbox.instance(script)
@@ -331,6 +329,5 @@ function sandbox.builtin_modules()
return builtin_modules
end
-
-- return module
return sandbox
diff --git a/xmake/core/tool/compiler.lua b/xmake/core/tool/compiler.lua
index e00505a4f..44349de5f 100644
--- a/xmake/core/tool/compiler.lua
+++ b/xmake/core/tool/compiler.lua
@@ -238,7 +238,7 @@ function compiler:build(sourcefiles, targetfile, opt)
if not targetkind and opt.target and opt.target.targetkind then
targetkind = opt.target:kind()
end
- return sandbox.load(self:_tool().build, self:_tool(), sourcefiles, targetkind or "binary", targetfile, flags, opt)
+ return sandbox.call(self:_tool().build, self:_tool(), sourcefiles, targetkind or "binary", targetfile, flags, opt)
end
-- get the build arguments list (compile and link)
@@ -297,7 +297,7 @@ function compiler:compile(sourcefiles, objectfile, opt)
opt = table.copy(opt)
opt.target = self:target()
profiler:enter(self:name(), "compile", sourcefiles)
- local ok, errors = sandbox.load(self:_tool().compile, self:_tool(), sourcefiles, objectfile, opt.dependinfo, compflags, opt)
+ local ok, errors = sandbox.call(self:_tool().compile, self:_tool(), sourcefiles, objectfile, opt.dependinfo, compflags, opt)
profiler:leave(self:name(), "compile", sourcefiles)
return ok, errors
end
diff --git a/xmake/core/tool/linker.lua b/xmake/core/tool/linker.lua
index 9cdff8840..7eaecc666 100644
--- a/xmake/core/tool/linker.lua
+++ b/xmake/core/tool/linker.lua
@@ -242,7 +242,7 @@ function linker:link(objectfiles, targetfile, opt)
opt = opt or {}
local linkflags = opt.linkflags or self:linkflags(opt)
profiler:enter(self:name(), "link", targetfile)
- local ok, errors = sandbox.load(self:_tool().link, self:_tool(),
+ local ok, errors = sandbox.call(self:_tool().link, self:_tool(),
table.wrap(objectfiles), self:_targetkind(), targetfile, linkflags,
table.join(opt, {target = self:target()}))
profiler:leave(self:name(), "link", targetfile)
diff --git a/xmake/core/tool/tool.lua b/xmake/core/tool/tool.lua
index c54e24c93..9d0dfa0d6 100644
--- a/xmake/core/tool/tool.lua
+++ b/xmake/core/tool/tool.lua
@@ -62,7 +62,7 @@ function _instance.new(kind, name, program, plat, arch, toolchain_inst)
-- init instance
if instance.init then
- local ok, errors = sandbox.load(instance.init, instance)
+ local ok, errors = sandbox.call(instance.init, instance)
if not ok then
return nil, errors
end
@@ -176,7 +176,7 @@ end
function _instance:_load_once()
if not self._LOADED then
if self.load then
- local ok, errors = sandbox.load(self.load, self)
+ local ok, errors = sandbox.call(self.load, self)
if not ok then
return false, errors
end
@@ -274,7 +274,7 @@ function tool.load(kind, opt)
tool._find_toolname = tool._find_toolname or import("lib.detect.find_toolname")
-- get the tool name from the program
- local ok, name_or_errors = sandbox.load(tool._find_toolname, toolname or program, {program = program})
+ local ok, name_or_errors = sandbox.call(tool._find_toolname, toolname or program, {program = program})
if not ok then
return nil, name_or_errors
end
diff --git a/xmake/core/tool/toolchain.lua b/xmake/core/tool/toolchain.lua
index 3bca7e37a..7b841a018 100644
--- a/xmake/core/tool/toolchain.lua
+++ b/xmake/core/tool/toolchain.lua
@@ -33,6 +33,7 @@ local hashset = require("base/hashset")
local scopeinfo = require("base/scopeinfo")
local interpreter = require("base/interpreter")
local is_cross = require("base/private/is_cross")
+local addon = require("package/addon")
local config = require("project/config")
local memcache = require("cache/memcache")
local localcache = require("cache/localcache")
@@ -356,7 +357,7 @@ function _instance:check()
if checked == nil then
local on_check = self:_on_check()
if on_check then
- local ok, results_or_errors = sandbox.load(on_check, self)
+ local ok, results_or_errors = sandbox.call(on_check, self)
if ok then
checked = results_or_errors
else
@@ -449,7 +450,7 @@ function _instance:_load()
local on_load = self:_on_load()
if on_load then
info:set("__loading", true)
- local ok, errors = sandbox.load(on_load, self)
+ local ok, errors = sandbox.call(on_load, self)
info:set("__loading", false)
if not ok then
os.raise(errors)
@@ -670,6 +671,28 @@ end
-- e.g. "mingw[clang]@llvm-mingw", "msvc[vs=2025,..]"
--
function toolchain.parsename(name)
+
+ -- the toolchain of an addon?
+ -- e.g. set_toolchains("@addon/esp32/xtensa"), set_toolchains("@addon/esp32/clang@llvm"), set_toolchains("@self/xtensa")
+ --
+ -- @note we need to parse it first, because `@` is also used for the toolchain packages, e.g. "@zig"
+ --
+ -- @note we only strip the `@addon/<addon>/` prefix here, the rest is parsed as usual,
+ -- so the addon toolchains can also be bound to packages, e.g. "@addon/esp32/clang@llvm"
+ --
+ local addon_prefix
+ if name:startswith("@addon/") then
+ local rest = name:sub(#"@addon/" + 1)
+ local pos = rest:find("/", 1, true)
+ if pos then
+ addon_prefix = "@addon/" .. rest:sub(1, pos - 1) .. "/"
+ name = rest:sub(pos + 1)
+ end
+ elseif name:startswith("@self/") then
+ addon_prefix = "@self/"
+ name = name:sub(#"@self/" + 1)
+ end
+
local splitinfo = name:split('@', {plain = true, strict = true})
local toolchain_name = splitinfo[1]
if toolchain_name == "" then
@@ -707,7 +730,8 @@ function toolchain.parsename(name)
end
end
end
- return {name = toolchain_name or packages, packages = packages, requireconfs = requireconfs, requirestr = requirestr}
+ return {name = toolchain_name or packages, packages = packages, addon_prefix = addon_prefix,
+ requireconfs = requireconfs, requirestr = requirestr}
end
-- get toolchain apis
@@ -781,9 +805,27 @@ function toolchain.load(name, opt)
configs.plat = opt.plat or config.get("plat") or os.host()
configs.arch = opt.arch or config.get("arch") or os.arch()
+ -- find the toolchain script path
+ --
+ -- @note we need to resolve the addon reference before the cache, `@self/` depends on
+ -- the addon which owns the caller script, and the different addons may provide
+ -- the same toolchain name
+ --
+ local scriptpath, addon_prefix
+ if parseinfo.addon_prefix then
+ -- e.g. set_toolchains("@addon/esp32/xtensa"), set_toolchains("@self/xtensa")
+ local referenceinfo, errors = addon.resolve_reference(parseinfo.addon_prefix .. name, "/", "toolchains",
+ {scriptdir = opt.scriptdir})
+ if not referenceinfo then
+ return nil, errors
+ end
+ addon_prefix = "@addon/" .. referenceinfo.addon .. "/"
+ scriptpath = path.join(referenceinfo.dir, referenceinfo.name, "xmake.lua")
+ end
+
-- get cache
local cache = toolchain._memcache()
- local cachekey = toolchain._cachekey(name, configs)
+ local cachekey = toolchain._cachekey((addon_prefix or "") .. name, configs)
-- get it directly from cache dirst
local instance = cache:get(cachekey)
@@ -791,16 +833,16 @@ function toolchain.load(name, opt)
return instance
end
- -- find the toolchain script path
- local scriptpath = nil
- for _, dir in ipairs(toolchain.directories()) do
- scriptpath = path.join(dir, name, "xmake.lua")
- if os.isfile(scriptpath) then
- break
+ if not addon_prefix then
+ for _, dir in ipairs(toolchain.directories()) do
+ scriptpath = path.join(dir, name, "xmake.lua")
+ if os.isfile(scriptpath) then
+ break
+ end
end
end
if not scriptpath or not os.isfile(scriptpath) then
- return nil, string.format("the toolchain %s not found!", name)
+ return nil, string.format("the toolchain %s%s not found!", addon_prefix or "", name)
end
-- get interpreter
diff --git a/xmake/languages/c++/xmake.lua b/xmake/languages/c++/xmake.lua
index b94a073b8..42ddc2884 100644
--- a/xmake/languages/c++/xmake.lua
+++ b/xmake/languages/c++/xmake.lua
@@ -60,8 +60,7 @@ language("c++")
, "toolchain.sysincludedirs"
}
, binary = {
- "target.runtimes"
- , "config.linkdirs"
+ "config.linkdirs"
, "config.frameworkdirs"
, "target.linkdirs"
, "target.frameworkdirs"
@@ -79,13 +78,16 @@ language("c++")
, "config.frameworks"
, "target.frameworks"
, "toolchain.frameworks"
+ -- runtimes may link libc++.a/libc++abi.a explicitly, so it must come after the
+ -- object files and user links, but before the syslinks it depends on (e.g. pthread)
+ -- @see https://github.com/xmake-io/xmake/issues/7442
+ , "target.runtimes"
, "config.syslinks"
, "target.syslinks"
, "toolchain.syslinks"
}
, shared = {
- "target.runtimes"
- , "config.linkdirs"
+ "config.linkdirs"
, "config.frameworkdirs"
, "target.linkdirs"
, "target.frameworkdirs"
@@ -103,6 +105,8 @@ language("c++")
, "config.frameworks"
, "target.frameworks"
, "toolchain.frameworks"
+ -- @see https://github.com/xmake-io/xmake/issues/7442
+ , "target.runtimes"
, "config.syslinks"
, "target.syslinks"
, "toolchain.syslinks"
diff --git a/xmake/languages/c/load.lua b/xmake/languages/c/load.lua
index a265f0dbd..7497cc004 100644
--- a/xmake/languages/c/load.lua
+++ b/xmake/languages/c/load.lua
@@ -61,6 +61,7 @@ function _get_apis()
, "package.add_defines"
, "package.add_undefines"
, "package.add_frameworks"
+ , "package.add_vectorexts"
, "package.add_rpathdirs"
, "package.add_linkdirs"
, "package.add_includedirs" --@note we need not uses paths for package, see https://github.com/xmake-io/xmake/issues/717
diff --git a/xmake/modules/cli/binutils/bin2obj.lua b/xmake/modules/cli/binutils/bin2obj.lua
index 06493143a..c8314594f 100644
--- a/xmake/modules/cli/binutils/bin2obj.lua
+++ b/xmake/modules/cli/binutils/bin2obj.lua
@@ -32,7 +32,8 @@ local options = {
{nil, "target_minver", "kv", nil, "Set the target minimum version (e.g., 10.0, 18.2)."},
{nil, "xcode_sdkver", "kv", nil, "Set the Xcode SDK version (e.g., 10.0, 18.2)."},
{nil, "zeroend", "k", nil, "Append a null terminator ('\\0') at the end of data."},
- {nil, "cosmocc", "k", nil, "Enable cosmocc support (generate concomitant object file)."}
+ {nil, "cosmocc", "k", nil, "Enable cosmocc support (generate concomitant object file)."},
+ {nil, "refobj", "kv", nil, "Mirror class/endianness/machine/e_flags from a reference elf object."}
}
function main(...)
diff --git a/xmake/modules/core/project/depend.lua b/xmake/modules/core/project/depend.lua
index 511e6fde6..40d6ba757 100644
--- a/xmake/modules/core/project/depend.lua
+++ b/xmake/modules/core/project/depend.lua
@@ -167,6 +167,9 @@ function is_changed(dependinfo, opt)
end
return true
elseif deptype == "table" then
+ if #depvalue ~= #optvalue then
+ return true
+ end
for subidx, subvalue in ipairs(depvalue) do
if subvalue ~= optvalue[subidx] then
if _is_show_diagnosis_info() then
diff --git a/xmake/modules/core/tools/clang.lua b/xmake/modules/core/tools/clang.lua
index 1250d1a46..c264f6656 100644
--- a/xmake/modules/core/tools/clang.lua
+++ b/xmake/modules/core/tools/clang.lua
@@ -192,6 +192,19 @@ function _has_static_libstdcxx(self)
return has_static_libstdcxx
end
+-- has -nostdlib++? (disable the automatic c++ runtime link, so we can link libc++/libc++abi explicitly)
+function _has_nostdlibxx(self)
+ local has_nostdlibxx = _g._HAS_NOSTDLIBXX
+ if has_nostdlibxx == nil then
+ if self:has_flags("-nostdlib++ -Werror", "ldflags", {flagskey = "clang_nostdlibxx"}) then
+ has_nostdlibxx = true
+ end
+ has_nostdlibxx = has_nostdlibxx or false
+ _g._HAS_NOSTDLIBXX = has_nostdlibxx
+ end
+ return has_nostdlibxx
+end
+
-- make the runtime flag
-- @see https://github.com/xmake-io/xmake/issues/3546
function nf_runtime(self, runtime, opt)
@@ -284,8 +297,36 @@ function nf_runtime(self, runtime, opt)
end
end
end
- if runtime:endswith("_static") and _has_static_libstdcxx(self) then
- maps["stdc++_static"] = table.join(maps["stdc++_static"], "-static-libstdc++")
+ if runtime:endswith("_static") then
+ -- -static-libstdc++ only pulls in libc++.a, not libc++abi.a, so the libc++abi
+ -- symbols (typeinfo, __cxa_*) stay undefined and the link fails, especially
+ -- once c++ modules reference more of libc++.
+ --
+ -- if we can locate both static archives, link them explicitly in a group
+ -- (they reference each other) and disable the driver's automatic c++ runtime
+ -- with -nostdlib++, otherwise (bundled abi) fall back to -static-libstdc++.
+ --
+ -- note: target.runtimes is ordered right before the syslinks in the c++ link
+ -- order, so these archives are placed after the object files / user links.
+ -- @see https://github.com/xmake-io/xmake/issues/7442, https://github.com/xmake-io/xmake/issues/7656
+ if llvm_dirs.libcxx_static and llvm_dirs.libcxxabi_static and _has_nostdlibxx(self) then
+ local cxxlibs = {llvm_dirs.libcxx_static, llvm_dirs.libcxxabi_static}
+ -- apple ld64 does not support --start-group, it always rescans archives
+ if not self:is_plat("macosx", "iphoneos", "watchos", "appletvos", "applexros") then
+ cxxlibs = table.join("-Wl,--start-group", cxxlibs, "-Wl,--end-group")
+ end
+ -- -stdlib=libc++ is unused when linking with -nostdlib++, remove it to avoid
+ -- the `argument unused during compilation` warning
+ local ldflags = table.remove_if(table.wrap(maps["c++_static"]), function (_, flag)
+ return flag == "-stdlib=libc++"
+ end)
+ maps["c++_static"] = table.join(ldflags, "-nostdlib++", cxxlibs)
+ elseif _has_static_libstdcxx(self) then
+ maps["c++_static"] = table.join(maps["c++_static"], "-static-libstdc++")
+ end
+ if _has_static_libstdcxx(self) then
+ maps["stdc++_static"] = table.join(maps["stdc++_static"], "-static-libstdc++")
+ end
end
end
end
diff --git a/xmake/modules/core/tools/ml.lua b/xmake/modules/core/tools/ml.lua
index 2e7628cd2..229a7303a 100644
--- a/xmake/modules/core/tools/ml.lua
+++ b/xmake/modules/core/tools/ml.lua
@@ -35,7 +35,7 @@ function init(self)
self:set("mapflags",
{
-- symbols
- ["-g"] = "-Z7"
+ ["-g"] = "-Zi"
, ["-fvisibility=.*"] = ""
-- warnings
@@ -55,20 +55,14 @@ function init(self)
end
-- make the symbol flags
+--
+-- masm only supports -Zi/-Zd, the -Z7 and -ZI flags of cl.exe are rejected (A4018).
+-- -Zi already embeds debug info in the object file (masm has no compile-time pdb),
+-- so the "embed" and "edit" levels degrade to it.
function nf_symbols(self, levels)
- local flags = nil
- local values = hashset.from(levels)
- if values:has("debug") then
- flags = {}
- if values:has("edit") then
- table.insert(flags, "-ZI")
- elseif values:has("embed") then
- table.insert(flags, "-Z7")
- else
- table.insert(flags, "-Zi")
- end
+ if hashset.from(levels):has("debug") then
+ return {"-Zi"}
end
- return flags
end
-- make the warning flag
diff --git a/xmake/modules/detect/sdks/find_mingw.lua b/xmake/modules/detect/sdks/find_mingw.lua
index c70bebb11..d6d7e0d8e 100644
--- a/xmake/modules/detect/sdks/find_mingw.lua
+++ b/xmake/modules/detect/sdks/find_mingw.lua
@@ -108,7 +108,8 @@ function _find_mingw(sdkdir, opt)
-- find cross toolchain
local toolchain = find_cross_toolchain(sdkdir or bindir, {bindir = bindir, cross = cross})
- if not toolchain then -- fallback, e.g. gcc.exe without cross
+ -- fallback, e.g. gcc.exe without cross
+ if not toolchain and (is_host("windows") or is_subhost("msys", "cygwin")) then
toolchain = find_cross_toolchain(sdkdir or bindir, {bindir = bindir})
end
if toolchain then
diff --git a/xmake/modules/detect/sdks/find_ndk.lua b/xmake/modules/detect/sdks/find_ndk.lua
index ce7a4b712..a89536d5f 100644
--- a/xmake/modules/detect/sdks/find_ndk.lua
+++ b/xmake/modules/detect/sdks/find_ndk.lua
@@ -95,7 +95,7 @@ function _find_ndk_sdkver(sdkdir, bindir, sysroot, arch)
-- try to select the best compatible version
local sdkver = "16"
if use_llvm or arch == "arm64-v8a" or arch == "riscv64" then
- sdkver = (arch == "riscv64") and "35" or "21"
+ sdkver = (arch == "riscv64") and "36" or "21"
end
if sysroot then
if os.isdir(path.join(sysroot, "usr", "lib", triple, sdkver)) then
diff --git a/xmake/modules/net/http/download.lua b/xmake/modules/net/http/download.lua
index 2695be817..e171c152d 100644
--- a/xmake/modules/net/http/download.lua
+++ b/xmake/modules/net/http/download.lua
@@ -297,18 +297,8 @@ function _powershell_download(tool, url, outputfile, opt)
os.vrunv(tool.program, argv)
end
--- download url
---
--- @param url the input url
--- @param outputfile the output file
--- @param opt the option, {continue = true}
---
---
-function main(url, outputfile, opt)
-
- -- init output file
- opt = opt or {}
- outputfile = outputfile or path.filename(url):gsub("%?.+$", "")
+-- download url with the first available tool (aria2/curl/wget/powershell)
+function _download(url, outputfile, opt)
-- attempt to download url using aria2 first (multi-threaded, fastest)
local tool = find_tool("aria2", {version = true})
@@ -338,3 +328,62 @@ function main(url, outputfile, opt)
assert(tool, "aria2, curl or wget not found!")
end
+
+-- is it a ssl/tls certificate verification error?
+function _is_ssl_cert_error(errors)
+ errors = (errors or ""):lower()
+ return errors:find("ssl", 1, true)
+ or errors:find("tls", 1, true)
+ or errors:find("certificate", 1, true)
+ or errors:find("handshake", 1, true)
+end
+
+-- download url, and retry once with ssl verification disabled on a certificate error
+function _download_fallback(url, outputfile, opt)
+ local errors
+ local ok = try
+ {
+ function ()
+ _download(url, outputfile, opt)
+ return true
+ end,
+ catch
+ {
+ function (errs)
+ errors = tostring(errs)
+ end
+ }
+ }
+ if not ok then
+ if _is_ssl_cert_error(errors) then
+ wprint("download failed due to ssl certificate verification, retrying with ssl verification disabled ..")
+ return _download(url, outputfile, table.join(opt, {insecure = true}))
+ end
+ raise(errors)
+ end
+end
+
+-- download url
+--
+-- @param url the input url
+-- @param outputfile the output file
+-- @param opt the option, e.g. {continue = true, insecure = false, insecure_fallback = false}
+--
+-- @note if opt.insecure_fallback is enabled and the download fails due to a ssl certificate
+-- error, it will retry once with ssl verification disabled. this is only safe when the
+-- caller verifies the downloaded file afterwards (e.g. by its sha256 checksum).
+--
+function main(url, outputfile, opt)
+
+ -- init output file
+ opt = opt or {}
+ outputfile = outputfile or path.filename(url):gsub("%?.+$", "")
+
+ -- download it directly if we do not need the insecure fallback
+ if opt.insecure or not opt.insecure_fallback then
+ return _download(url, outputfile, opt)
+ end
+
+ -- download it with the insecure fallback
+ return _download_fallback(url, outputfile, opt)
+end
diff --git a/xmake/modules/package/manager/install_package.lua b/xmake/modules/package/manager/install_package.lua
index a2c605ba7..55458ca83 100644
--- a/xmake/modules/package/manager/install_package.lua
+++ b/xmake/modules/package/manager/install_package.lua
@@ -46,6 +46,8 @@ function _install_package(manager_name, package_name, opt)
table.insert(managers, "vcpkg")
table.insert(managers, "brew")
table.insert(managers, "nix")
+ elseif is_host("bsd") then
+ table.insert(managers, "pkg") -- freebsd/dragonflybsd
end
assert(#managers > 0, "no suitable package manager!")
diff --git a/xmake/modules/package/manager/pkg/install_package.lua b/xmake/modules/package/manager/pkg/install_package.lua
new file mode 100644
index 000000000..8d04f92b3
--- /dev/null
+++ b/xmake/modules/package/manager/pkg/install_package.lua
@@ -0,0 +1,62 @@
+--!A cross-platform build utility based on Lua
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+-- Copyright (C) 2015-present, Xmake Open Source Community.
+--
+-- @author ruki
+-- @file install_package.lua
+--
+
+-- imports
+import("core.base.option")
+import("lib.detect.find_tool")
+import("privilege.sudo")
+
+-- install package
+--
+-- @param name the package name
+-- @param opt the options, e.g. {verbose = true, pkg = "the package name"}
+--
+-- @return true or false
+--
+function main(name, opt)
+
+ -- init options
+ opt = opt or {}
+
+ -- find pkg, e.g. freebsd, dragonflybsd
+ local pkg = find_tool("pkg")
+ if not pkg then
+ raise("pkg not found!")
+ end
+
+ -- init argv
+ local argv = {"install", "-y", opt.pkg or name}
+
+ -- install package directly if the current user is root
+ if os.isroot() then
+ os.vrunv(pkg.program, argv)
+ -- install with administrator permission?
+ elseif sudo.has() then
+
+ -- install it if be confirmed
+ local description = format("try installing %s with administrator permission", name)
+ local confirm = utils.confirm({default = true, description = description})
+ if confirm then
+ sudo.vrunv(pkg.program, argv)
+ end
+ else
+ raise("cannot get administrator permission!")
+ end
+end
diff --git a/xmake/modules/package/manager/vcpkg/find_package.lua b/xmake/modules/package/manager/vcpkg/find_package.lua
index 5b515d8a2..0504070a3 100644
--- a/xmake/modules/package/manager/vcpkg/find_package.lua
+++ b/xmake/modules/package/manager/vcpkg/find_package.lua
@@ -212,7 +212,6 @@ function _find_package(vcpkg, vcpkgdir, name, opt)
if required_features then
depend_name = name .. "[" .. table.concat(required_features, ",") .. "]"
end
- local result = nil
local argv = {"depend-info", depend_name, "--sort=reverse", "--triplet=" .. triplet}
-- pass feature flags to depend-info when in manifest mode, otherwise depend-info will not show the complete dependency tree with features
@@ -220,31 +219,44 @@ function _find_package(vcpkg, vcpkgdir, name, opt)
table.insert(argv, 1, "--feature-flags=versions")
end
- local _, dependinfo = try { function () return os.iorunv(vcpkg, argv, manifest_mode and {curdir = opt.installdir} or nil) end }
+ local _, dependinfo = try { function () return os.iorunv(vcpkg, argv, {curdir = manifest_mode and opt.installdir or vcpkg_utils.classic_curdir()}) end }
if manifest_mode and not dependinfo then
- -- fallback: newer vcpkg-tool no longer accepts the package name as a positional argument,
- -- drop it and retry. see https://github.com/microsoft/vcpkg-tool/pull/1909
+ -- fallback: in manifest mode vcpkg rejects the package name as a positional argument, so
+ -- drop it and query the manifest's dependency tree instead.
+ -- see https://github.com/microsoft/vcpkg-tool/pull/1909
table.remove(argv, 3)
_, dependinfo = try { function () return os.iorunv(vcpkg, argv, {curdir = opt.installdir}) end }
end
+
+ -- collect the packages to read info from: always the main package (its .list file was found
+ -- above, so it is known-installed), plus any transitive dependencies reported by depend-info.
+ -- this keeps resolution working even when depend-info fails, e.g. a project-owned vcpkg.json
+ -- puts vcpkg in manifest mode and it rejects arguments; only transitive deps are then missing.
+ -- @see https://github.com/xmake-io/xmake/issues/7660
+ local packagenames = {name}
if dependinfo then
for _, line in ipairs(dependinfo:split("\n", {plain = true})) do
if not line:startswith("vcpkg-") then
local packagename = line:match("^([^%[:]+)[^:]*:")
if packagename then
- local dependencyresult = _get_package_info(packagename, triplet, infodirs, arch, plat, mode)
- if dependencyresult then
- result = result or {}
- for key, dependencylist in pairs(dependencyresult) do
- result[key] = result[key] or {}
- table.join2(result[key], dependencylist)
- end
- end
+ table.insert(packagenames, packagename)
end
end
end
end
+ local result = nil
+ for _, packagename in ipairs(table.unique(packagenames)) do
+ local dependencyresult = _get_package_info(packagename, triplet, infodirs, arch, plat, mode)
+ if dependencyresult then
+ result = result or {}
+ for key, dependencylist in pairs(dependencyresult) do
+ result[key] = result[key] or {}
+ table.join2(result[key], dependencylist)
+ end
+ end
+ end
+
-- save version
if result then
local infoname = path.basename(infofile)
diff --git a/xmake/modules/package/manager/vcpkg/install_package.lua b/xmake/modules/package/manager/vcpkg/install_package.lua
index a8546e5d0..909374e61 100644
--- a/xmake/modules/package/manager/vcpkg/install_package.lua
+++ b/xmake/modules/package/manager/vcpkg/install_package.lua
@@ -64,7 +64,10 @@ function _install_for_classic(vcpkg, name, opt)
end
-- install package
- os.vrunv(vcpkg, argv)
+ -- run in a neutral directory so that a project-owned vcpkg.json in the current working
+ -- directory does not switch vcpkg into manifest mode (which rejects package arguments).
+ -- @see https://github.com/xmake-io/xmake/issues/7660
+ os.vrunv(vcpkg, argv, {curdir = vcpkg_utils.classic_curdir()})
end
-- install for manifest mode
diff --git a/xmake/modules/package/manager/vcpkg/utils.lua b/xmake/modules/package/manager/vcpkg/utils.lua
index fdd85d501..1c83323b4 100644
--- a/xmake/modules/package/manager/vcpkg/utils.lua
+++ b/xmake/modules/package/manager/vcpkg/utils.lua
@@ -36,6 +36,22 @@ function need_manifest(opt)
end
end
+-- get a neutral empty directory to run classic-mode vcpkg commands in.
+--
+-- vcpkg switches to manifest mode when a vcpkg.json exists in the current working directory, and
+-- in manifest mode it rejects individual package arguments ("In manifest mode, `vcpkg install`
+-- does not support individual package arguments"). so classic-mode commands (install/list/
+-- depend-info, which pass `<pkg>:<triplet>`) must not run from a project directory that ships its
+-- own vcpkg.json.
+-- @see https://github.com/xmake-io/xmake/issues/7660
+function classic_curdir()
+ local dir = path.join(os.tmpdir(), "vcpkg", "classic")
+ if not os.isdir(dir) then
+ os.mkdir(dir)
+ end
+ return dir
+end
+
function is_installed(vcpkg, name, triplet, opt)
local argv = {"list", name .. ":" .. triplet, "--x-full-desc"}
local manifest_mode = need_manifest(opt)
@@ -46,7 +62,7 @@ function is_installed(vcpkg, name, triplet, opt)
end
local listinfo = try { function ()
- return os.iorunv(vcpkg, argv, manifest_mode and {curdir = opt.installdir} or nil)
+ return os.iorunv(vcpkg, argv, {curdir = manifest_mode and opt.installdir or classic_curdir()})
end}
if listinfo then
local exact_prefix = name .. ":" .. triplet
diff --git a/xmake/modules/package/manager/xmake/search_package.lua b/xmake/modules/package/manager/xmake/search_package.lua
index 32991ae3e..a5ed3caf8 100644
--- a/xmake/modules/package/manager/xmake/search_package.lua
+++ b/xmake/modules/package/manager/xmake/search_package.lua
@@ -23,7 +23,7 @@ import("core.base.semver")
import("private.xrepo.quick_search.cache")
function _search_package(packages, name, opt)
- for _, packageinfo in ipairs(cache.find(name, {description = opt.description ~= false})) do
+ for _, packageinfo in ipairs(cache.find(name, {description = opt.description ~= false, kind = opt.kind})) do
local packagename = packageinfo.name
local packagedata = packageinfo.data
@@ -59,7 +59,7 @@ end
-- search package using the xmake package manager
--
-- @param name the package name with pattern
--- @param opt the options, e.g. {require_version = "1.x"}
+-- @param opt the options, e.g. {require_version = "1.x", kind = "addon"}
--
function main(name, opt)
opt = opt or {}
diff --git a/xmake/modules/package/tools/cmake.lua b/xmake/modules/package/tools/cmake.lua
index 360a0f700..311b1c0af 100644
--- a/xmake/modules/package/tools/cmake.lua
+++ b/xmake/modules/package/tools/cmake.lua
@@ -858,17 +858,32 @@ function _get_envs_for_default_flags(package, configs, opt)
return table.clone(_get_default_flags(package, configs, buildtype, opt)) or {}
end
+-- quote flags that contain whitespace, so they survive as a single argument when cmake expands
+-- CMAKE_<LANG>_FLAGS (a space-separated string) onto the compiler/linker command line.
+-- e.g. -resource-dir=C:\Program Files\LLVM\lib\clang\22 would otherwise be split at the space.
+-- @see https://github.com/xmake-io/xmake/issues/7663
+function _quote_flags_with_spaces(flags)
+ if flags then
+ for idx, flag in ipairs(flags) do
+ if type(flag) == "string" and flag:find("%s") and not flag:startswith("\"") then
+ flags[idx] = "\"" .. flag .. "\""
+ end
+ end
+ end
+ return flags
+end
+
function _get_envs_for_runtime_flags(package, opt)
local buildtype = _get_cmake_buildtype(package)
local envs = {}
local runtimes = package:runtimes()
if runtimes then
- envs[format("CMAKE_C_FLAGS_%s", buildtype)] = toolchain_utils.map_compflags_for_package(package, "c", "runtime", runtimes)
- envs[format("CMAKE_CXX_FLAGS_%s", buildtype)] = toolchain_utils.map_compflags_for_package(package, "cxx", "runtime", runtimes)
- envs[format("CMAKE_EXE_LINKER_FLAGS_%s", buildtype)] = toolchain_utils.map_linkflags_for_package(package, "binary", {"cxx"}, "runtime", runtimes)
- envs[format("CMAKE_STATIC_LINKER_FLAGS_%s", buildtype)] = toolchain_utils.map_linkflags_for_package(package, "static", {"cxx"}, "runtime", runtimes)
- envs[format("CMAKE_SHARED_LINKER_FLAGS_%s", buildtype)] = toolchain_utils.map_linkflags_for_package(package, "shared", {"cxx"}, "runtime", runtimes)
- envs[format("CMAKE_MODULE_LINKER_FLAGS_%s", buildtype)] = toolchain_utils.map_linkflags_for_package(package, "shared", {"cxx"}, "runtime", runtimes)
+ envs[format("CMAKE_C_FLAGS_%s", buildtype)] = _quote_flags_with_spaces(toolchain_utils.map_compflags_for_package(package, "c", "runtime", runtimes))
+ envs[format("CMAKE_CXX_FLAGS_%s", buildtype)] = _quote_flags_with_spaces(toolchain_utils.map_compflags_for_package(package, "cxx", "runtime", runtimes))
+ envs[format("CMAKE_EXE_LINKER_FLAGS_%s", buildtype)] = _quote_flags_with_spaces(toolchain_utils.map_linkflags_for_package(package, "binary", {"cxx"}, "runtime", runtimes))
+ envs[format("CMAKE_STATIC_LINKER_FLAGS_%s", buildtype)] = _quote_flags_with_spaces(toolchain_utils.map_linkflags_for_package(package, "static", {"cxx"}, "runtime", runtimes))
+ envs[format("CMAKE_SHARED_LINKER_FLAGS_%s", buildtype)] = _quote_flags_with_spaces(toolchain_utils.map_linkflags_for_package(package, "shared", {"cxx"}, "runtime", runtimes))
+ envs[format("CMAKE_MODULE_LINKER_FLAGS_%s", buildtype)] = _quote_flags_with_spaces(toolchain_utils.map_linkflags_for_package(package, "shared", {"cxx"}, "runtime", runtimes))
end
return envs
end
@@ -1285,8 +1300,11 @@ function _shrink_cmake_arguments(argv, oldir, opt)
shrink = true
return true
end]]
- -- shrink long arguments
- if #v > 128 then
+ -- shrink long arguments, or arguments that carry quoted flags (e.g. a path with spaces).
+ -- routing them through set() in CMakeLists.txt keeps the embedded quotes intact, instead
+ -- of relying on the fragile multi-layer quoting of passing -D...="..." on the cmake cli.
+ -- @see https://github.com/xmake-io/xmake/issues/7663
+ if #v > 128 or v:find("\"", 1, true) then
local flags = v:replace("\"", "\\\"")
table.insert(cmake_argv, ("set(%s \"%s\")"):format(k, flags))
shrink = true
diff --git a/xmake/modules/package/tools/xmake.lua b/xmake/modules/package/tools/xmake.lua
index 4efee0974..305074df0 100644
--- a/xmake/modules/package/tools/xmake.lua
+++ b/xmake/modules/package/tools/xmake.lua
@@ -283,6 +283,14 @@ function _get_configs(package, configs, opt)
if not package:use_external_includes() and (not policies or not policies:find("package.include_external_headers", 1, true)) then
table.insert(policies_list, "package.include_external_headers:n")
end
+ -- the sub-process must install its packages locally too, otherwise they go to the
+ -- global directory while we expect them under our build directory,
+ -- @see https://github.com/xmake-io/xmake/issues/7716
+ for _, policyname in ipairs({"package.install_locally", "package.host.install_locally"}) do
+ if project.policy(policyname) and (not policies or not policies:find(policyname, 1, true)) then
+ table.insert(policies_list, policyname)
+ end
+ end
if policies and policies:find("package.build.ccache", 1, true) then
table.insert(configs, "--ccachedir=" .. path.join(path.directory(package:cachedir()), "build_cache"))
table.insert(policies_list, "build.ccache")
@@ -527,14 +535,19 @@ function install(package, configs, opt)
-- get build environments
local envs = opt.envs or buildenvs(package)
- -- if the package is installed locally, pass the local packages directory
- -- to the child xmake process so it can find already-installed deps
- -- without re-installing them to the global directory
+ -- if the package is installed locally, pass our local packages directory to the
+ -- child xmake process, so the packages it installs locally land in the same place
+ -- and the deps we have already installed are found instead of installed again
+ --
+ -- @note we must not override `XMAKE_PKG_INSTALLDIR` here: it is the *global* root
+ -- of the child, and overriding it hides `~/.xmake/packages` from it, so the host
+ -- packages it needs (e.g. the toolchains) would be installed again under our
+ -- build directory, @see https://github.com/xmake-io/xmake/issues/7716
+ --
-- @see https://github.com/xmake-io/xmake/discussions/7441
if package:is_local() and not package:is_source_embed() then
envs = table.clone(envs)
- envs.XMAKE_PKG_INSTALLDIR = package_core.installdir({localdir = true})
- envs.XMAKE_PKG_CACHEDIR = package_core.cachedir({localdir = true})
+ envs.XMAKE_PKG_LOCALDIR = package_core.installdir({localdir = true})
end
-- pass local repositories
diff --git a/xmake/modules/private/action/addon/impl/install_addons.lua b/xmake/modules/private/action/addon/impl/install_addons.lua
new file mode 100644
index 000000000..4cc5b0fba
--- /dev/null
+++ b/xmake/modules/private/action/addon/impl/install_addons.lua
@@ -0,0 +1,142 @@
+--!A cross-platform build utility based on Lua
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+-- Copyright (C) 2015-present, Xmake Open Source Community.
+--
+-- @author ruki
+-- @file install_addons.lua
+--
+
+-- imports
+import("core.package.addon")
+import("core.project.addons")
+import("private.action.addon.impl.xrepo", {alias = "xrepo_addon"})
+
+-- get the requires of the declared addons
+--
+-- @note we install the locked versions, but the declaration is authoritative, so we
+-- resolve them again if the user has changed it or upgrades them
+--
+function _get_requires(declared, locked)
+ local requires = {}
+ for _, requirestr in ipairs(declared) do
+ local name = addons.requirename(requirestr)
+ local lockinfo = locked and locked[name]
+ if addons.locked_valid(requirestr, lockinfo) then
+ requirestr = name .. " " .. lockinfo.version
+ end
+ table.insert(requires, requirestr)
+ end
+ return requires
+end
+
+-- lock the installed addons, so that we always get the same ones
+--
+-- @note we get the installed versions from the addons registry, they are
+-- registered when installing them, @see core/package/addon.lua
+--
+function _lock_addons(projectdir, declared)
+ local lockinfo = {}
+ local locked = addons.locked(projectdir) or {}
+
+ -- @note we need to reload the registry, they have been installed by another process,
+ -- and we must not see them through the locked versions, we are locking them right now,
+ -- e.g. `xmake addon --upgrade` pins the old ones before loading this project
+ local installed = addon.addons({force = true, unpinned = true})
+ for _, requirestr in ipairs(declared) do
+ local name = addons.requirename(requirestr)
+ local addoninfo = assert(installed[addon.dirname(name)], "addon(%s) is not installed!", name)
+ local oldversion = locked[name] and locked[name].version
+ if oldversion and oldversion ~= addoninfo.version then
+ cprint("${color.success}upgrade ${bright}%s${clear}: %s -> %s", name, oldversion, addoninfo.version)
+ end
+ -- we lock the repository too, so that the other users get it from the same source,
+ -- @see xmake/modules/private/action/require/impl/lock_packages.lua
+ lockinfo[name] = {version = addoninfo.version, repo = addoninfo.repo}
+ end
+ lockinfo.__meta__ = {version = addons.lockfile_version()}
+
+ -- @note we need to write it deterministically, the key order of a lua table is random,
+ -- otherwise the lock file would change even if nothing changed,
+ -- @see xmake/modules/private/action/require/impl/lock_packages.lua
+ local content = string.serialize(lockinfo, {orderkeys = true})
+ local tmpfile = os.tmpfile()
+ io.writefile(tmpfile, content, {encoding = "binary"})
+
+ -- and we only write it if the content is different, so we can keep the file time
+ os.cp(tmpfile, addons.lockfile(projectdir), {copy_if_different = true})
+ os.rm(tmpfile)
+end
+
+-- install the addons which a project declares, e.g. add_addons("esp32-devel 1.0.x")
+--
+-- @note we are also called from a sub-process, the project cannot be loaded until
+-- its addons are installed, @see core/project/project.lua
+--
+-- install the addons which a project declares, e.g. add_addons("esp32-devel 1.0.x")
+--
+-- @param projectdir the project directory, we only read/write its lock file here
+-- @param datafile the declarations of the project, {addons = {...}, repositories = {...}}
+--
+-- @note we are always run in a working directory which has no project, we cannot load
+-- the project again here, @see xmake/core/project/project.lua
+--
+function main(projectdir, datafile, opt)
+ opt = opt or {}
+ projectdir = projectdir or os.projectdir()
+ local declarations = type(datafile) == "table" and datafile or io.load(datafile)
+ local declared = table.wrap(declarations and declarations.addons)
+ if #declared == 0 then
+ return
+ end
+
+ -- upgrade them? we need to resolve the declared versions again
+ local locked = not opt.upgrade and addons.locked(projectdir) or nil
+
+ -- this project declares its own repositories? we pass them to xrepo,
+ -- they are only used by this installation, we do not register them globally
+ local rcfile
+ local repositories = table.wrap(declarations.repositories)
+ if #repositories > 0 then
+ rcfile = os.tmpfile() .. ".lua"
+ local file = io.open(rcfile, "w")
+ for _, repo in ipairs(repositories) do
+ file:print("add_repositories(%q)", repo)
+ end
+ file:close()
+ end
+
+ -- install them with xrepo, it installs the packages in its own working directory
+ try
+ {
+ function ()
+ xrepo_addon("install", _get_requires(declared, locked), {includes = rcfile})
+ end,
+ finally
+ {
+ -- @note try() swallows the errors if we do not re-raise them here
+ function (ok, errors)
+ if rcfile then
+ os.tryrm(rcfile)
+ end
+ if not ok then
+ raise(errors)
+ end
+ end
+ }
+ }
+
+ -- and lock them, so that the other users get the same versions
+ _lock_addons(projectdir, declared)
+end
diff --git a/xmake/modules/private/action/addon/impl/xrepo.lua b/xmake/modules/private/action/addon/impl/xrepo.lua
new file mode 100644
index 000000000..46ada2d01
--- /dev/null
+++ b/xmake/modules/private/action/addon/impl/xrepo.lua
@@ -0,0 +1,57 @@
+--!A cross-platform build utility based on Lua
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+-- Copyright (C) 2015-present, Xmake Open Source Community.
+--
+-- @author ruki
+-- @file xrepo.lua
+--
+
+-- imports
+import("core.base.option")
+import("core.package.addon")
+
+-- run the given xrepo action for the addons
+--
+-- @note xrepo installs the packages in its own working project, so it works anywhere,
+-- and we need not implement the download/dependencies/confirm logic again
+--
+-- @param action the action name, e.g. "install", "search"
+-- @param names the addon names, urls or require strings, e.g. {"esp32-devel 1.0.x"}
+-- @param opt the options, e.g. {force = true, includes = "/tmp/xxx.lua"}
+--
+-- @note we always run it in a working directory which has no project, @see addon.workdir()
+--
+function main(action, names, opt)
+ opt = opt or {}
+ local argv = {"lua", "private.xrepo", action, "--addon"}
+
+ -- we need to pass the common options to the sub-process, e.g. -y, -v, -D
+ for _, name in ipairs({"yes", "verbose", "diagnosis"}) do
+ if option.get(name) then
+ table.insert(argv, "--" .. name)
+ end
+ end
+ if opt.force then
+ table.insert(argv, "--force")
+ end
+
+ -- the extra lua configuration files, e.g. the repositories which a project declares
+ if opt.includes then
+ table.insert(argv, "--includes=" .. opt.includes)
+ end
+
+ table.join2(argv, names)
+ os.execv(os.programfile(), argv, {curdir = opt.curdir or addon.workdir()})
+end
diff --git a/xmake/modules/private/action/build/pcheader.lua b/xmake/modules/private/action/build/pcheader.lua
index 4e984f62b..1682ead8c 100644
--- a/xmake/modules/private/action/build/pcheader.lua
+++ b/xmake/modules/private/action/build/pcheader.lua
@@ -38,12 +38,24 @@ function config(target, langkind, opt)
-- https://github.com/xmake-io/xmake/issues/2667
-- https://github.com/xmake-io/xmake/issues/5858
if not os.isfile(headerfile) then
- io.writefile(headerfile, ([[
+ local langcxx = (langkind == "cxx" or langkind == "mxx")
+ local content
+ if langcxx then
+ content = [[
#pragma system_header
#ifdef __cplusplus
#include "%s"
#endif // __cplusplus
- ]]):format(path.absolute(pcheaderfile):gsub("\\", "/")))
+ ]]
+ else
+ content = [[
+#pragma system_header
+#ifndef __cplusplus
+#include "%s"
+#endif
+ ]]
+ end
+ io.writefile(headerfile, content:format(path.absolute(pcheaderfile):gsub("\\", "/")))
end
-- we need only to add a header wrapper in .gch directory
-- @see https://github.com/xmake-io/xmake/issues/5858#issuecomment-2506918167
diff --git a/xmake/modules/private/action/require/impl/actions/download.lua b/xmake/modules/private/action/require/impl/actions/download.lua
index 55e43a3db..a8846f97b 100644
--- a/xmake/modules/private/action/require/impl/actions/download.lua
+++ b/xmake/modules/private/action/require/impl/actions/download.lua
@@ -216,6 +216,7 @@ function _download(package, url, sourcedir, opt)
else
http.download(url, packagefile, {
insecure = global.get("insecure-ssl"),
+ insecure_fallback = true, -- retry without ssl verification on cert error, the file is verified by sha256 below
headers = opt.url_http_headers or package:policy("package.download.http_headers")})
end
end
diff --git a/xmake/modules/private/action/require/impl/actions/download_resources.lua b/xmake/modules/private/action/require/impl/actions/download_resources.lua
index c185d25b0..dffa742b2 100644
--- a/xmake/modules/private/action/require/impl/actions/download_resources.lua
+++ b/xmake/modules/private/action/require/impl/actions/download_resources.lua
@@ -118,6 +118,7 @@ function _download(package, resource_name, resource_url, resource_hash)
elseif resource_url:find(string.ipattern("https-://")) or resource_url:find(string.ipattern("ftps-://")) then
http.download(resource_url, resource_file, {
insecure = global.get("insecure-ssl"),
+ insecure_fallback = true, -- retry without ssl verification on cert error, the file is verified by sha256 below
headers = package:policy("package.download.http_headers")})
else
raise("invalid resource url(%s)", resource_url)
diff --git a/xmake/modules/private/action/require/impl/actions/install.lua b/xmake/modules/private/action/require/impl/actions/install.lua
index 7fb54a951..c07add337 100644
--- a/xmake/modules/private/action/require/impl/actions/install.lua
+++ b/xmake/modules/private/action/require/impl/actions/install.lua
@@ -22,6 +22,7 @@
import("core.base.option")
import("core.base.tty")
import("core.package.package", {alias = "core_package"})
+import("core.package.addon")
import("core.project.target")
import("core.project.project")
import("core.platform.platform")
@@ -283,6 +284,50 @@ function _merge_staticlibs(package)
end
end
+-- register the installed addon, so that xmake can find its payloads, e.g. plugins
+--
+-- @note the addon manifest(addon.lua) is only read when installing it, everything which
+-- is needed later is recorded in the addons registry, @see core/package/addon.lua
+--
+function _register_addon(package)
+
+ -- get the addon deps from the package recipe
+ local deps
+ for _, dep in ipairs(package:plaindeps() or {}) do
+ if dep:is_addon() then
+ deps = deps or {}
+ table.insert(deps, dep:name())
+ end
+ end
+
+ -- the addon describes itself? we prefer its own description
+ --
+ -- @note the deps are duplicated in its manifest, but the recipe is authoritative,
+ -- xmake needs them before downloading the addon sources, so we only report the
+ -- mismatch, they must be kept in sync
+ --
+ local description = package:description()
+ local manifest = package:data("addon.manifest")
+ if manifest then
+ description = manifest.description or description
+ for _, dep in ipairs(manifest.deps) do
+ if not (deps and table.contains(deps, dep)) then
+ wprint("addon(%s): dep(%s) is declared in its manifest, but not in the package recipe!",
+ package:name(), dep)
+ end
+ end
+ end
+
+ -- we also record where it comes from, so that the projects can lock it,
+ -- @see xmake/modules/private/action/addon/impl/install_addons.lua
+ local repo = package:repo()
+ addon.register(package:name(), package:version_str() or "latest",
+ {description = description, deps = deps,
+ repo = repo and {url = repo:url(), commit = repo:commit(), branch = repo:branch()} or nil,
+ manifest_deps = manifest and manifest.deps or nil,
+ globalmodules = manifest and #manifest.globalmodules > 0 and manifest.globalmodules or nil})
+end
+
-- get failed install directory
function _get_installdir_failed(package)
return path.join(package:cachedir(), "installdir.failed")
@@ -499,6 +544,11 @@ function main(package)
-- save the package info to the manifest file
package:manifest_save()
+
+ -- register this addon, so that xmake can find its payloads, e.g. plugins
+ if package:is_addon() then
+ _register_addon(package)
+ end
installed_now = true
end
end
@@ -568,6 +618,13 @@ function main(package)
end
os.tryrm(installdir)
+ -- the addon is registered before testing it, its `on_test` needs to use it,
+ -- e.g. xmake create -t esp32.blink, so we need to unregister it again here,
+ -- otherwise it would be seen as installed while its files are gone
+ if package:is_addon() then
+ addon.unregister(package:name(), package:version_str() or "latest")
+ end
+
-- is not last scheme? we can fallback to next scheme and try reinstall it again
local current_scheme = package:current_scheme()
local schemes_orderlist = package:schemes_orderlist()
diff --git a/xmake/modules/private/action/require/impl/actions/patch_sources.lua b/xmake/modules/private/action/require/impl/actions/patch_sources.lua
index 6c18b69b1..6b8e60e59 100644
--- a/xmake/modules/private/action/require/impl/actions/patch_sources.lua
+++ b/xmake/modules/private/action/require/impl/actions/patch_sources.lua
@@ -80,6 +80,7 @@ function _patch(package, patchinfo)
if patch_url:find(string.ipattern("https-://")) or patch_url:find(string.ipattern("ftps-://")) then
http.download(patch_url, patch_file, {
insecure = global.get("insecure-ssl"),
+ insecure_fallback = true, -- retry without ssl verification on cert error, the file is verified by sha256 below
headers = package:policy("package.download.http_headers")})
else
-- copy the patch file
diff --git a/xmake/modules/private/action/require/impl/install_packages.lua b/xmake/modules/private/action/require/impl/install_packages.lua
index f0da9eb2e..2d633109e 100644
--- a/xmake/modules/private/action/require/impl/install_packages.lua
+++ b/xmake/modules/private/action/require/impl/install_packages.lua
@@ -167,6 +167,13 @@ function _get_confirm_from_3rd(packages)
end
-- get user confirm
+--
+-- @param packages the packages to be installed
+-- @param opt the options
+-- - toolchain: these packages are toolchain packages, we will show a different tip for it
+--
+-- @return the confirm result and the modified packages
+--
function _get_confirm(packages, opt)
opt = opt or {}
@@ -204,6 +211,8 @@ function _get_confirm(packages, opt)
-- show tips
if opt.toolchain then
cprint("${bright color.warning}note: ${clear}install or modify (m) these ${bright}toolchain${clear} packages first (pass -y to skip confirm)?")
+ elseif opt.packagekind == "addon" then
+ cprint("${bright color.warning}note: ${clear}install or modify (m) these ${bright}addons${clear} (pass -y to skip confirm)?")
else
cprint("${bright color.warning}note: ${clear}install or modify (m) these packages (pass -y to skip confirm)?")
end
@@ -683,6 +692,11 @@ function _get_package_installdeps(packages)
end
-- install packages
+--
+-- @param requires the package requires, e.g. {"zlib >=1.2.11", "libpng"}
+-- @param opt the options, @see main
+-- - toolchain: only install the toolchain packages and their dependent packages
+--
function _install_packages(requires, opt)
opt = opt or {}
@@ -845,8 +859,15 @@ end
-- install all required packages
--
--- @param requires the requires table
--- @param opt the options
+-- @param requires the package requires, e.g. {"zlib >=1.2.11", "libpng"}
+-- @param opt the options, it will be passed to `package.load_packages` directly
+-- - requires_extra: the extra require configs from `add_requires()`, indexed by the require string
+-- - nodeps: only install the given packages, do not install their dependent packages
+-- - system: load package from system if `true`, and never load it if `false` (only for non-3rd packages)
+-- - packagekind: the package kind, e.g. "addon", it will be loaded from the `addons` root directory of repositories
+-- @note `toolchain` is reserved and it will be set internally, @see load_packages
+--
+-- @return the installed packages, including the toolchain packages and all dependent packages
--
function main(requires, opt)
-- we need to install toolchain packages first,
diff --git a/xmake/modules/private/action/require/impl/package.lua b/xmake/modules/private/action/require/impl/package.lua
index 3ad758c3a..1105384c8 100644
--- a/xmake/modules/private/action/require/impl/package.lua
+++ b/xmake/modules/private/action/require/impl/package.lua
@@ -43,6 +43,15 @@ function _memcache()
end
-- load require info
+--
+-- @param require_str the require string, e.g. "zlib >=1.2.11", "libplist[shared,debug]"
+-- @param requires_extra the extra require configs from `add_requires()`, indexed by the require string
+-- @param opt the options
+-- - requirepath: the parent require path, e.g. "foo.bar", it's used to get the resolved requireinfo
+-- - resolvedinfo: the resolved requireinfo of dependency conflicts, indexed by require path
+--
+-- @return the package name and requireinfo
+--
function _load_require(require_str, requires_extra, opt)
opt = opt or {}
@@ -183,6 +192,15 @@ function _load_package_from_project(packagename)
end
-- load package package from repositories
+--
+-- @param packagename the package name
+-- @param opt the options
+-- - plat: the given platform of this package
+-- - arch: the given architecture of this package
+-- - name: the given repository name, we will only find this package in the given repository
+-- - rootdir: the root directory of repositories, e.g. "packages" (default), "addons"
+-- - locked_repo: the locked repository info in `xmake-requires.lock`, e.g. {url = .., commit = .., branch = ..}
+--
function _load_package_from_repository(packagename, opt)
opt = opt or {}
local packagedir, repo = repository.packagedir(packagename, opt)
@@ -191,7 +209,31 @@ function _load_package_from_repository(packagename, opt)
end
end
+-- get the root directory of repositories for the given package
+--
+-- e.g. "packages" (default), "addons", "plugins" (deprecated)
+--
+-- @note the addon packages are only searched from the `addons` root directory,
+-- and we need to set it explicitly, e.g. add_deps("foo", {kind = "addon"})
+--
+function _get_repository_rootdir(requireinfo, opt)
+ local packagekind = requireinfo.kind or opt.packagekind
+ if packagekind == "addon" then
+ return "addons"
+ elseif packagekind == "plugin" then
+ return "plugins"
+ end
+ return "packages"
+end
+
-- load package package from base
+--
+-- e.g. package("foo") set_base("bar")
+--
+-- @param package the package instance
+-- @param basename the base package name
+-- @param opt the options, @see _load_package_from_repository
+--
function _load_package_from_base(package, basename, opt)
local package_base = _load_package_from_project(basename)
if not package_base then
@@ -203,6 +245,10 @@ function _load_package_from_base(package, basename, opt)
end
-- has locked requires?
+--
+-- @param opt the options
+-- - force: force to use the locked requires even if `xmake require --upgrade` is called
+--
function _has_locked_requires(opt)
opt = opt or {}
if not option.get("upgrade") or opt.force then
@@ -211,6 +257,13 @@ function _has_locked_requires(opt)
end
-- get locked requires
+--
+-- @param requirekey the require key in `xmake-requires.lock`, @see _get_packagelock_key
+-- @param opt the options
+-- - force: force to reload `xmake-requires.lock` and ignore `--upgrade`
+--
+-- @return the locked requireinfo and the version of `xmake-requires.lock`
+--
function _get_locked_requires(requirekey, opt)
opt = opt or {}
local requireslock = _memcache():get("requireslock")
@@ -260,6 +313,10 @@ end
--
-- orderdeps: a -> b -> c
--
+-- @param package the package instance
+-- @param opt the options
+-- - private: also sort the private library deps, e.g. add_deps("foo", {private = true})
+--
function _sort_librarydeps(package, opt)
-- we must use native deps list instead of package:deps() to generate correct link order
local orderdeps = {}
@@ -564,6 +621,13 @@ function _match_requirepath(requirepath, requireconf)
end
-- init requireinfo
+--
+-- @param requireinfo the requireinfo
+-- @param package the package instance
+-- @param opt the options
+-- - is_toplevel: this package is a toplevel package in `add_requires()`, but not a dependent package,
+-- and we will pass some root configs to it, e.g. toolchains, runtimes, lto, asan ..
+--
function _init_requireinfo(requireinfo, package, opt)
-- pass root configs to top library package
requireinfo.configs = requireinfo.configs or {}
@@ -899,11 +963,35 @@ function _select_package_runtimes(package)
end
end
--- load required packages
+-- load the given required package
+--
+-- we will load it from the project, repositories and system in order,
+-- and the requireinfo will be initialized and attached to the package instance.
+--
+-- @param packagename the package name, e.g. "zlib", "zlib~debug", "vcpkg::zlib"
+-- @param requireinfo the requireinfo, @see _load_require
+-- @param opt the options
+-- - system: load package from system if `true`, and never load it if `false`,
+-- it's only used when `add_requires("zlib", {system = nil})` is not set (only for non-3rd packages)
+-- - packagekind: the package kind, e.g. "addon", it will be loaded from the `addons` root directory of repositories
+-- - toolchain: only load toolchain packages, the non-toolchain toplevel packages will be ignored
+-- - requirepath: the current require path, e.g. "foo.bar", it's used to detect circular dependencies
+-- and match `add_requireconfs()`
+-- - parentinfo: the parent requireinfo, this package will inherit some builtin configs from it, e.g. runtimes, pic
+--
+-- @return the package instance, it will be nil if this package is filtered by `opt.toolchain`,
+-- and it will raise an error if this package is not found in any repositories
+--
function _load_package(packagename, requireinfo, opt)
-- check circular dependency
opt = opt or {}
+
+ -- the `addon` and `self` names are reserved, we use them to reference the addon resources,
+ -- e.g. add_rules("@addon/esp32/flash"), import("@self.sdkconfig")
+ if packagename == "addon" or packagename == "self" then
+ raise("package(%s): the name `%s` is reserved by xmake for the addon references, please rename it!", packagename, packagename)
+ end
if opt.requirepath then
local splitinfo = opt.requirepath:split(".", {plain = true})
if #splitinfo > 3 and
@@ -949,6 +1037,7 @@ function _load_package(packagename, requireinfo, opt)
plat = requireinfo.plat,
arch = requireinfo.arch,
name = requireinfo.reponame,
+ rootdir = _get_repository_rootdir(requireinfo, opt),
locked_repo = locked_requireinfo and locked_requireinfo.repo})
if package then
from_repo = true
@@ -958,7 +1047,9 @@ function _load_package(packagename, requireinfo, opt)
-- load base package
if package and package:get("base") then
_load_package_from_base(package, package:get("base"), {
- name = requireinfo.reponame, locked_repo = locked_requireinfo and locked_requireinfo.repo})
+ name = requireinfo.reponame,
+ rootdir = _get_repository_rootdir(requireinfo, opt),
+ locked_repo = locked_requireinfo and locked_requireinfo.repo})
end
-- load package from system
@@ -1105,7 +1196,14 @@ function _load_package(packagename, requireinfo, opt)
return package
end
--- load all required packages
+-- load all required packages and their dependent packages
+--
+-- @param requires the package requires, e.g. {"zlib >=1.2.11", "libpng"}
+-- @param opt the options, @see load_packages
+--
+-- @return the packages with all dependent packages (the deps are always in front of their parents),
+-- and the packages without deps
+--
function _load_packages(requires, opt)
-- no requires?
@@ -1420,6 +1518,11 @@ end
-- compatible with all previous link dependencies?
-- @see https://github.com/xmake-io/xmake/issues/2719
+--
+-- @param package the package instance
+-- @param opt the options
+-- - install_finished: the installation has been finished, we do not need to check compatibility again
+--
function _compatible_with_previous_librarydeps(package, opt)
-- skip to check compatibility if installation has been finished
@@ -1524,6 +1627,13 @@ function cachedir()
end
-- this package should be install?
+--
+-- @param package the package instance
+-- @param opt the options
+-- - install_finished: the installation has been finished, it's used to check if this package
+-- has been installed successfully, and we will ignore `package.install_always`
+-- policy and the librarydeps compatibility checking
+--
function should_install(package, opt)
opt = opt or {}
if package:is_template() then
@@ -1592,6 +1702,9 @@ function get_configs_str(package)
end
if requireinfo.kind then
table.insert(configs, requireinfo.kind)
+ elseif package:is_addon() then
+ -- @note the kind is only set for the dependencies, e.g. add_deps("foo", {kind = "addon"})
+ table.insert(configs, "addon")
end
local ignored_configs_for_buildhash = hashset.from(requireinfo.ignored_configs_for_buildhash or {})
local configs_overrided = requireinfo.configs_overrided or {}
@@ -1631,7 +1744,15 @@ function get_configs_str(package)
return configs_str
end
--- get locked requireinfo
+-- get locked requireinfo from `xmake-requires.lock`
+--
+-- @param requireinfo the requireinfo, it must contain the requirekey
+-- @param opt the options
+-- - force: force to reload `xmake-requires.lock` and ignore `--upgrade`
+--
+-- @return the locked requireinfo and the version of `xmake-requires.lock`,
+-- it will be nil if the lock file does not exist or its version is incompatible
+--
function get_locked_requireinfo(requireinfo, opt)
local requirekey = requireinfo.requirekey
local locked_requireinfo, requireslock_version
@@ -1645,6 +1766,13 @@ function get_locked_requireinfo(requireinfo, opt)
end
-- load requires
+--
+-- @param requires the package requires, e.g. {"zlib >=1.2.11", "libpng"}
+-- @param requires_extra the extra require configs from `add_requires()`, indexed by the require string
+-- @param opt the options, @see _load_require
+--
+-- @return the require items, e.g. {{name = "zlib", info = {version = ">=1.2.11", ..}}, ..}
+--
function load_requires(requires, requires_extra, opt)
opt = opt or {}
local requireitems = {}
@@ -1656,6 +1784,18 @@ function load_requires(requires, requires_extra, opt)
end
-- load all required packages
+--
+-- @param requires the package requires, e.g. {"zlib >=1.2.11", "libpng"}
+-- @param opt the options
+-- - requires_extra: the extra require configs from `add_requires()`, e.g. {["zlib >=1.2.11"] = {configs = {shared = true}}}
+-- - nodeps: only load the given packages, do not load their dependent packages
+-- - system: load package from system if `true`, and never load it if `false` (only for non-3rd packages)
+-- - packagekind: the package kind, e.g. "addon", it will be loaded from the `addons` root directory of repositories
+-- - toolchain: only load toolchain packages and their dependent packages
+-- - requirepath: the parent require path, e.g. "foo.bar", it's used to detect circular dependencies and match `add_requireconfs()`
+-- - parentinfo: the parent requireinfo, the child package will inherit some builtin configs from it, e.g. runtimes, pic
+-- - resolvedinfo: the resolved requireinfo of dependency conflicts, it's only used to reload packages internally
+--
function load_packages(requires, opt)
opt = opt or {}
local unique = {}
diff --git a/xmake/modules/private/action/require/impl/repository.lua b/xmake/modules/private/action/require/impl/repository.lua
index 3df59c42c..d17bdb570 100644
--- a/xmake/modules/private/action/require/impl/repository.lua
+++ b/xmake/modules/private/action/require/impl/repository.lua
@@ -151,6 +151,9 @@ function pulled()
end
-- get package directory from repositories
+--
+-- @param packagename the package name
+-- @param opt {rootdir = "packages|addons|plugins"}
function packagedir(packagename, opt)
-- strip trailing ~tag, e.g. zlib~debug
@@ -162,7 +165,8 @@ function packagedir(packagename, opt)
-- get cache key
local reponame = opt.name
- local cachekey = packagename
+ local rootdir = opt.rootdir or "packages"
+ local cachekey = rootdir .. "/" .. packagename
local locked_repo = opt.locked_repo
if locked_repo then
cachekey = cachekey .. locked_repo.url .. (locked_repo.commit or "") .. (locked_repo.branch or "")
@@ -185,7 +189,7 @@ function packagedir(packagename, opt)
-- find the package directory from repositories
if not foundir then
for _, repo in ipairs(repositories()) do
- local dir = path.join(repo:directory(), "packages", packagename:sub(1, 1), packagename)
+ local dir = path.join(repo:directory(), rootdir, packagename:sub(1, 1), packagename)
if os.isdir(dir) and os.isfile(path.join(dir, "xmake.lua")) and (not reponame or reponame == repo:name()) then
foundir = {dir, repo}
break
diff --git a/xmake/modules/private/action/require/install.lua b/xmake/modules/private/action/require/install.lua
index b7ba9ef5e..ebc648124 100644
--- a/xmake/modules/private/action/require/install.lua
+++ b/xmake/modules/private/action/require/install.lua
@@ -82,7 +82,14 @@ function main(requires_raw)
-- install packages
environment.enter()
- local packages = install_packages(requires, {requires_extra = requires_extra})
+ -- @note the `--plugin` option is deprecated, please use `--addon` instead
+ local packagekind = "package"
+ if option.get("addon") then
+ packagekind = "addon"
+ elseif option.get("plugin") then
+ packagekind = "plugin"
+ end
+ local packages = install_packages(requires, {packagekind = packagekind, requires_extra = requires_extra})
if packages then
_check_missing_packages(packages)
end
diff --git a/xmake/modules/private/action/require/search.lua b/xmake/modules/private/action/require/search.lua
index e714c6c19..a0f98e0d9 100644
--- a/xmake/modules/private/action/require/search.lua
+++ b/xmake/modules/private/action/require/search.lua
@@ -20,6 +20,7 @@
-- imports
import("core.base.task")
+import("core.base.option")
import("private.action.require.impl.utils.filter")
import("private.action.require.impl.repository")
import("private.action.require.impl.environment")
@@ -41,11 +42,14 @@ function main(names)
task.run("repo", {update = true})
end
+ -- we only search the addon packages if `--addon` is enabled
+ local kind = option.get("addon") and "addon" or nil
+
-- show title
- print("The package names:")
+ print(kind == "addon" and "The addon names:" or "The package names:")
-- search packages
- for name, packages in pairs(search_packages(names)) do
+ for name, packages in pairs(search_packages(names, {kind = kind})) do
if #packages > 0 then
-- show name
diff --git a/xmake/modules/private/check/checkers/api/package/kind.lua b/xmake/modules/private/check/checkers/api/package/kind.lua
index 4a9c309e3..2e4f2495b 100644
--- a/xmake/modules/private/check/checkers/api/package/kind.lua
+++ b/xmake/modules/private/check/checkers/api/package/kind.lua
@@ -37,6 +37,8 @@ function main(opt)
end
return true
end
- return value == "binary" or value == "toolchain" or value == "template"
+ -- @note the `plugin` kind is deprecated, please use the `addon` kind instead
+ return value == "binary" or value == "toolchain" or value == "template" or
+ value == "addon" or value == "plugin"
end}))
end
diff --git a/xmake/modules/private/utils/batchcmds.lua b/xmake/modules/private/utils/batchcmds.lua
index 3f584b286..349e31efe 100644
--- a/xmake/modules/private/utils/batchcmds.lua
+++ b/xmake/modules/private/utils/batchcmds.lua
@@ -28,6 +28,7 @@ import("core.theme.theme")
import("core.tool.linker")
import("core.tool.compiler")
import("core.language.language")
+import("core.sandbox.sandbox")
import("utils.run_script")
import("utils.progress", {alias = "progress_utils"})
import("utils.binary.rpath", {alias = "rpath_utils"})
@@ -126,6 +127,20 @@ function _runcmd_vlua(cmd, opt)
end
end
+-- run command: call
+function _runcmd_call(cmd, opt)
+ local func = cmd.func
+ if func then
+ if not opt.dryrun then
+ local argv = table.clone(cmd.argv)
+ if cmd.opt then
+ table.insert(argv, cmd.opt)
+ end
+ func(table.unpack(argv))
+ end
+ end
+end
+
-- run command: os.mkdir
function _runcmd_mkdir(cmd, opt)
local dir = cmd.dir
@@ -222,6 +237,7 @@ function _runcmd(cmd, opt)
vexecv = _runcmd_vexecv,
lua = _runcmd_lua,
vlua = _runcmd_vlua,
+ call = _runcmd_call,
mkdir = _runcmd_mkdir,
rmdir = _runcmd_rmdir,
cd = _runcmd_cd,
@@ -305,11 +321,23 @@ function batchcmds:lua(script, argv, opt)
table.insert(self:cmds(), {kind = "lua", script = script, argv = argv, opt = opt})
end
--- add command: run lua script file, command or module
+-- add command: run lua script, command or module
function batchcmds:vlua(script, argv, opt)
table.insert(self:cmds(), {kind = "vlua", script = script, argv = argv, opt = opt})
end
+-- add command: call lua function
+function batchcmds:call(func, argv, opt)
+ local functype = type(func)
+ if functype == "string" then
+ self:lua(func, argv, opt)
+ else
+ assert(functype == "function")
+ sandbox.fork(func)
+ table.insert(self:cmds(), {kind = "call", func = func, argv = argv, opt = opt})
+ end
+end
+
-- add command: compile source files
--
-- @param sourcefiles the source file paths
diff --git a/xmake/modules/private/utils/toolchain.lua b/xmake/modules/private/utils/toolchain.lua
index c2840a591..3b4fe8296 100644
--- a/xmake/modules/private/utils/toolchain.lua
+++ b/xmake/modules/private/utils/toolchain.lua
@@ -343,6 +343,25 @@ function map_linkflags_for_package(package, targetkind, sourcekinds, name, value
return flags
end
+-- ask the clang driver for the absolute path of the given library
+--
+-- clang knows its own library search paths, e.g. the per-target runtime directory
+-- (`lib/<target-triple>/libc++.a`), and it also works when the toolchain has no `--sdk=/xxx/llvm`.
+--
+-- it just echoes back the given filename if the library does not exist,
+-- so we only accept an existing absolute path.
+function _get_llvm_libfile(toolchain, clang, libname, opt)
+ local argv = table.wrap(get_clang_target_flags(toolchain))
+ table.insert(argv, "-print-file-name=" .. libname)
+ local libfile = try {function () return os.iorunv(clang, argv, {envs = opt.envs}) end}
+ if libfile then
+ libfile = path.normalize(libfile:trim())
+ if path.is_absolute(libfile) and os.isfile(libfile) then
+ return libfile
+ end
+ end
+end
+
-- Check and cache llvm/clang driver info (e.g. -print-resource-dir/-print-target-triple) in toolchain:config().
-- It must be collected during toolchain on_check so that toolchain on_load can use it without running clang again.
-- @see https://github.com/xmake-io/xmake/issues/7337#issuecomment-3942499208
@@ -370,6 +389,17 @@ function check_llvm_info(toolchain, clang, opt)
toolchain:config_set("llvm_target_triple", target_triple)
end
end
+
+ -- the static c++ runtime archives, they may be in the per-target runtime directory,
+ -- e.g. `lib/x86_64-unknown-linux-gnu/libc++.a`
+ -- @see https://github.com/xmake-io/xmake/issues/7442
+ for libname, configname in pairs({["libc++.a"] = "llvm_libcxx_static", ["libc++abi.a"] = "llvm_libcxxabi_static"}) do
+ local libfile = _get_llvm_libfile(toolchain, clang, libname, opt)
+ if libfile then
+ dprint("checking for llvm %s ... %s", libname, libfile)
+ toolchain:config_set(configname, libfile)
+ end
+ end
end
-- get llvm sdk resource directory
@@ -457,6 +487,11 @@ function get_llvm_dirs(toolchain)
end
local bindir, libdir, cxxlibdir, includedir, cxxincludedir, resourcedir, rtdir, rtlink, rtlibdir
+
+ -- the clang driver has already resolved the static c++ runtime archives on check
+ -- @see check_llvm_info
+ local libcxx_static = toolchain:config("llvm_libcxx_static")
+ local libcxxabi_static = toolchain:config("llvm_libcxxabi_static")
if rootdir then
bindir = path.join(rootdir, "bin")
bindir = os.isdir(bindir) and bindir or nil
@@ -476,6 +511,19 @@ function get_llvm_dirs(toolchain)
end
end
+ -- fallback to search the static c++ runtime archives in the library directories
+ if libdir and not (libcxx_static and libcxxabi_static) then
+ local staticdirs = cxxlibdir and {cxxlibdir, libdir} or {libdir}
+ for _, dir in ipairs(staticdirs) do
+ if not libcxx_static and os.isfile(path.join(dir, "libc++.a")) then
+ libcxx_static = path.join(dir, "libc++.a")
+ end
+ if not libcxxabi_static and os.isfile(path.join(dir, "libc++abi.a")) then
+ libcxxabi_static = path.join(dir, "libc++abi.a")
+ end
+ end
+ end
+
includedir = path.join(rootdir, "include")
includedir = os.isdir(includedir) and includedir or nil
@@ -502,7 +550,9 @@ function get_llvm_dirs(toolchain)
resourcedir = resourcedir,
rtdir = rtdir,
rtlibdir = rtlibdir,
- rtlink = rtlink }
+ rtlink = rtlink,
+ libcxx_static = libcxx_static,
+ libcxxabi_static = libcxxabi_static }
memcache:set(cachekey, llvm_dirs)
end
return llvm_dirs
@@ -645,7 +695,7 @@ function get_zig_target(toolchain)
end
if toolchain:is_plat("cross") then
- -- xmake f -p cross --toolchain=zig --cross=mips64el-linux-gnuabi64
+ -- xmake f -p cross --toolchain=zigcc --cross=mips64el-linux-gnuabi64
elseif toolchain:is_plat("macosx") then
--@see https://github.com/ziglang/zig/issues/14226
target = arch .. "-macos-none"
diff --git a/xmake/modules/private/xrepo/action/install.lua b/xmake/modules/private/xrepo/action/install.lua
index b0d297b38..4abb57c7f 100644
--- a/xmake/modules/private/xrepo/action/install.lua
+++ b/xmake/modules/private/xrepo/action/install.lua
@@ -47,6 +47,9 @@ function menu_options()
"e.g.",
" - xrepo install -p cross --toolchain=mytool --includes='toolchain1.lua" .. path.envsep() .. "toolchain2.lua'"},
{nil, "policies", "kv", nil, "Set the policies." },
+ {nil, "addon", "k", nil, "Install addon packages from <repository>/addons/"},
+ {nil, "plugin", "k", nil, "Install plugin packages from <repository>/plugins/",
+ "(deprecated, please use --addon instead)"},
{category = "Visual Studio SDK Configuration" },
{nil, "vs", "kv", nil, "The Microsoft Visual Studio"
, " e.g. --vs=2017" },
@@ -281,6 +284,12 @@ function _install_packages(packages)
if option.get("build") or is_debug then
table.insert(require_argv, "--build")
end
+ if option.get("addon") then
+ table.insert(require_argv, "--addon")
+ end
+ if option.get("plugin") then
+ table.insert(require_argv, "--plugin")
+ end
local extra = {system = false}
if mode == "debug" then
extra.debug = true
diff --git a/xmake/modules/private/xrepo/action/remove.lua b/xmake/modules/private/xrepo/action/remove.lua
index ed92a83b8..eed956c6e 100644
--- a/xmake/modules/private/xrepo/action/remove.lua
+++ b/xmake/modules/private/xrepo/action/remove.lua
@@ -20,6 +20,7 @@
-- imports
import("core.base.option")
+import("core.package.addon")
import("private.action.require.impl.remove_packages", {alias = "remove_all_packages"})
-- get menu options
@@ -44,6 +45,10 @@ function menu_options()
{nil, "toolchain", "kv", nil, "Set the toolchain name." },
{nil, "toolchain_host", "kv", nil, "Set the host toolchain name." },
{ },
+ {nil, "addon", "k", nil, "Remove the given installed addon packages.",
+ "e.g.",
+ " - xrepo remove --addon serial-monitor" },
+ {'f', "force", "k", nil, "Force to remove the addon packages, even if they are depended on by the others." },
{nil, "all", "k", nil, "Remove all packages and ignore extra package configs.",
"If `--all` is enabled, the package name parameter will support lua pattern",
"e.g.",
@@ -193,10 +198,21 @@ function _remove_packages(packages)
os.vexecv(os.programfile(), require_argv)
end
+-- remove the given installed addons
+function _remove_addons(names)
+ for _, name in ipairs(names) do
+ addon.remove(name, {force = option.get("force")})
+ cprint("${color.success}remove ${bright}%s${clear} ok!", name)
+ end
+end
+
-- main entry
function main()
local packages = option.get("packages")
- if option.get("all") then
+ if option.get("addon") then
+ assert(packages, "please specify the addons to be removed.")
+ _remove_addons(packages)
+ elseif option.get("all") then
remove_all_packages(packages)
elseif packages then
_remove_packages(packages)
diff --git a/xmake/modules/private/xrepo/action/search.lua b/xmake/modules/private/xrepo/action/search.lua
index d389e3833..c532af44e 100644
--- a/xmake/modules/private/xrepo/action/search.lua
+++ b/xmake/modules/private/xrepo/action/search.lua
@@ -30,6 +30,9 @@ function menu_options()
-- menu options
local options =
{
+ {nil, "addon", "k", nil, "Search the addon packages from <repository>/addons/",
+ "e.g.",
+ " - xrepo search --addon serial"},
{nil, "packages", "vs", nil, "The packages list (support lua pattern).",
"e.g.",
" - xrepo search zlib boost",
@@ -80,6 +83,9 @@ function _search_packages(packages)
if option.get("diagnosis") then
table.insert(require_argv, "-D")
end
+ if option.get("addon") then
+ table.insert(require_argv, "--addon")
+ end
table.join2(require_argv, packages)
os.vexecv(os.programfile(), require_argv)
end
diff --git a/xmake/modules/private/xrepo/quick_search/cache.lua b/xmake/modules/private/xrepo/quick_search/cache.lua
index cf75a212d..feba8cda9 100644
--- a/xmake/modules/private/xrepo/quick_search/cache.lua
+++ b/xmake/modules/private/xrepo/quick_search/cache.lua
@@ -25,20 +25,36 @@ import("private.action.require.impl.repository")
local cache = globalcache.cache("quick_search")
+-- get the cache key of the given package
+--
+-- @note the addons are stored with the `addon::` prefix,
+-- because an addon and a package may have the same name
+--
+function _cachekey(packagename, kind)
+ return kind == "addon" and ("addon::" .. packagename) or packagename
+end
+
-- search package directories from repositories
+--
+-- the packages are stored in <repodir>/packages/<first-letter>/<name>,
+-- and the addons are stored in <repodir>/addons/<first-letter>/<name>
+--
function _list_package_dirs()
-- find the package directories from all repositories
local unique = {}
local packageinfos = {}
for _, repo in ipairs(repository.repositories()) do
- for _, file in ipairs(os.files(path.join(repo:directory(), "packages", "*", "*", "xmake.lua"))) do
- local dir = path.directory(file)
- local subdirname = path.basename(path.directory(dir))
- if #subdirname == 1 then -- ignore l/luajit/port/xmake.lua
- local packagename = path.filename(dir)
- if not unique[packagename] then
- table.insert(packageinfos, {name = packagename, repo = repo, packagedir = dir})
- unique[packagename] = true
+ for _, rootinfo in ipairs({{rootdir = "packages"}, {rootdir = "addons", kind = "addon"}}) do
+ for _, file in ipairs(os.files(path.join(repo:directory(), rootinfo.rootdir, "*", "*", "xmake.lua"))) do
+ local dir = path.directory(file)
+ local subdirname = path.basename(path.directory(dir))
+ if #subdirname == 1 then -- ignore l/luajit/port/xmake.lua
+ local packagename = path.filename(dir)
+ local cachekey = _cachekey(packagename, rootinfo.kind)
+ if not unique[cachekey] then
+ table.insert(packageinfos, {name = packagename, kind = rootinfo.kind, repo = repo, packagedir = dir})
+ unique[cachekey] = true
+ end
end
end
end
@@ -57,7 +73,9 @@ end
function update()
for _, packageinfo in ipairs(_list_package_dirs()) do
local package = core_package.load_from_repository(packageinfo.name, packageinfo.packagedir, {repo = packageinfo.repo})
- cache:set(packageinfo.name, {
+ cache:set(_cachekey(packageinfo.name, packageinfo.kind), {
+ name = packageinfo.name,
+ kind = packageinfo.kind,
reponame = package:repo() and package:repo():name(),
description = package:description(),
versions = package:versions(),
@@ -79,22 +97,30 @@ function get()
end
-- find package
+--
+-- @param name the package name (support lua pattern)
+-- @param opt the options, e.g. {prefix = true, description = true, kind = "addon"}
+--
function find(name, opt)
_init()
opt = opt or {}
local list_result = {}
- for packagename, packagedata in pairs(cache:data()) do
- local found = false
- if opt.prefix then
- found = packagename:startswith(name)
- else
- found = packagename:find(path.pattern(name))
- end
- if not found and opt.description and packagedata.description and packagedata.description:find(name) then
- found = true
- end
- if found then
- table.insert(list_result, {name = packagename, data = packagedata})
+ for cachekey, packagedata in table.orderpairs(cache:data()) do
+ -- we only search the packages with the given kind, e.g. nil (package), "addon"
+ local packagename = packagedata.name or cachekey
+ if packagedata.kind == opt.kind then
+ local found = false
+ if opt.prefix then
+ found = packagename:startswith(name)
+ else
+ found = packagename:find(path.pattern(name))
+ end
+ if not found and opt.description and packagedata.description and packagedata.description:find(name) then
+ found = true
+ end
+ if found then
+ table.insert(list_result, {name = packagename, data = packagedata})
+ end
end
end
return list_result
diff --git a/xmake/platforms/bsd/xmake.lua b/xmake/platforms/bsd/xmake.lua
index 98143d019..89ab580fe 100644
--- a/xmake/platforms/bsd/xmake.lua
+++ b/xmake/platforms/bsd/xmake.lua
@@ -21,7 +21,7 @@
platform("bsd")
set_os("bsd")
set_hosts("bsd")
- set_archs("i386", "x86_64")
+ set_archs("i386", "x86_64", "arm", "arm64", "ppc", "ppc64", "ppc64el", "riscv64", "sparc64")
set_formats("static", "lib$(name).a")
set_formats("object", "$(name).o")
@@ -53,5 +53,3 @@ platform("bsd")
, {nil, "qt_host", "kv", "auto", "The Qt Host SDK Directory" }
}
}
-
-
diff --git a/xmake/platforms/cross/xmake.lua b/xmake/platforms/cross/xmake.lua
index 98bb31634..462fc961b 100644
--- a/xmake/platforms/cross/xmake.lua
+++ b/xmake/platforms/cross/xmake.lua
@@ -20,7 +20,7 @@
platform("cross")
set_hosts("macosx", "linux", "windows", "bsd")
- set_archs("i386", "x86_64", "arm", "arm64", "mips", "mips64", "riscv", "riscv64", "loong64", "s390x", "ppc", "ppc64", "sh4")
+ set_archs("i386", "x86_64", "arm", "armv7", "arm64", "mips", "mips64", "mips64el", "riscv", "riscv64", "loong64", "s390x", "ppc", "ppc64", "ppc64el", "sh4", "sparc64")
set_formats("static", "lib$(name).a")
set_formats("object", "$(name).o")
@@ -28,5 +28,3 @@ platform("cross")
set_formats("symbol", "$(name).sym")
set_toolchains("envs", "cross")
-
-
diff --git a/xmake/platforms/linux/xmake.lua b/xmake/platforms/linux/xmake.lua
index c4d09880e..c26f99fb5 100644
--- a/xmake/platforms/linux/xmake.lua
+++ b/xmake/platforms/linux/xmake.lua
@@ -21,7 +21,7 @@
platform("linux")
set_os("linux")
set_hosts("macosx", "linux", "windows", "bsd")
- set_archs("i386", "x86_64", "armv7", "armv7s", "arm64", "mips", "mips64", "mipsel", "mips64el", "loong64")
+ set_archs("i386", "x86_64", "armv7", "armv7s", "arm64", "mips", "mips64", "mipsel", "mips64el", "loong64", "riscv64", "s390x", "ppc", "ppc64", "ppc64el", "sparc64")
set_formats("static", "lib$(name).a")
set_formats("object", "$(name).o")
diff --git a/xmake/platforms/solaris/xmake.lua b/xmake/platforms/solaris/xmake.lua
index 5abdde4fb..260bc138b 100644
--- a/xmake/platforms/solaris/xmake.lua
+++ b/xmake/platforms/solaris/xmake.lua
@@ -21,7 +21,7 @@
platform("solaris")
set_os("solaris")
set_hosts("solaris")
- set_archs("i386", "x86_64")
+ set_archs("i386", "x86_64", "sparc64")
set_formats("static", "lib$(name).a")
set_formats("object", "$(name).o")
@@ -53,5 +53,3 @@ platform("solaris")
, {nil, "qt_host", "kv", "auto", "The Qt Host SDK Directory" }
}
}
-
-
diff --git a/xmake/plugins/doxygen/main.lua b/xmake/plugins/doxygen/main.lua
index 80fd77675..b84754cb2 100644
--- a/xmake/plugins/doxygen/main.lua
+++ b/xmake/plugins/doxygen/main.lua
@@ -71,6 +71,10 @@ end
function main()
+ -- @note we cannot use utils.warning() here, it's queued and only shown at the end
+ cprint("${bright color.warning}${text.warning}: ${color.warning}the builtin `xmake doxygen` plugin is deprecated, " ..
+ "please use the doxygen-plugin addon: `xmake addon --install doxygen-plugin`")
+
-- load configuration
config.load()
diff --git a/xmake/plugins/format/main.lua b/xmake/plugins/format/main.lua
index 408de0421..c8f50a15e 100644
--- a/xmake/plugins/format/main.lua
+++ b/xmake/plugins/format/main.lua
@@ -103,6 +103,10 @@ end
-- main
function main()
+ -- @note we cannot use utils.warning() here, it's queued and only shown at the end
+ cprint("${bright color.warning}${text.warning}: ${color.warning}the builtin `xmake format` plugin is deprecated, " ..
+ "please use the format-plugin addon: `xmake addon --install format-plugin`")
+
-- load configuration
config.load()
diff --git a/xmake/plugins/macro/main.lua b/xmake/plugins/macro/main.lua
index 86b50e2da..6c2c8c734 100644
--- a/xmake/plugins/macro/main.lua
+++ b/xmake/plugins/macro/main.lua
@@ -309,6 +309,10 @@ end
-- main
function main()
+ -- @note we cannot use utils.warning() here, it's queued and only shown at the end
+ cprint("${bright color.warning}${text.warning}: ${color.warning}the builtin `xmake macro` plugin is deprecated, " ..
+ "please use the macro-plugin addon: `xmake addon --install macro-plugin`")
+
-- list macros
if option.get("list") then
diff --git a/xmake/plugins/pack/wix/main.lua b/xmake/plugins/pack/wix/main.lua
index e12461883..f91c15273 100644
--- a/xmake/plugins/pack/wix/main.lua
+++ b/xmake/plugins/pack/wix/main.lua
@@ -326,7 +326,7 @@ function _pack_wix(wix, package)
table.join2(argv, {"-ext", "WixToolset.UI.wixext"})
table.join2(argv, {"-o", package:outputfile()})
- if package:arch() == "x64" then
+ if package:is_arch("x64", "x86_64") then
table.join2(argv, {"-arch", "x64"})
elseif package:arch() == "x86" then
table.join2(argv, {"-arch", "x86"})
diff --git a/xmake/plugins/plugin/main.lua b/xmake/plugins/plugin/main.lua
index b362381e4..ce1ac3e4f 100644
--- a/xmake/plugins/plugin/main.lua
+++ b/xmake/plugins/plugin/main.lua
@@ -21,146 +21,235 @@
-- imports
import("core.base.option")
import("core.base.global")
+import("core.package.repository")
import("devel.git")
-import("net.fasturl")
import("private.action.require.impl.environment")
+import("private.action.require.impl.install_packages")
--- get plugin urls
-function _plugin_urls()
- local urls = option.get("plugins")
- if urls then
- local result = {}
- for _, url in ipairs(urls) do
- table.insert(result, git.asgiturl(url) or url)
- end
- urls = result
- else
- urls = {
- "https://github.com/xmake-io/xmake-plugins.git",
- "https://gitlab.com/tboox/xmake-plugins.git",
- "https://gitee.com/tboox/xmake-plugins.git"}
- urls = fasturl.add(urls)
- urls = fasturl.sort(urls)
+-- validate a plugin directory name
+function _check_plugin_name(name)
+ assert(type(name) == "string" and name ~= "" and name ~= "." and not name:find("..", 1, true) and not name:find("[/\\:]"), "invalid plugin name(%s)!", name)
+ return name
+end
+
+-- get plugin directory in ~/.xmake/plugins
+function _get_plugindir(name)
+ local plugindir = path.join(global.directory(), "plugins")
+ return name and path.join(plugindir, _check_plugin_name(name)) or plugindir
+end
+
+-- get local and global repositories, with local taking precedence
+function _repositories()
+ return table.join(repository.repositories({global = false}), repository.repositories({global = true}))
+end
+
+-- install a plugin from the given repository or the first repository containing it
+function _install_plugins_from_repo(name, reponame)
+
+ -- check plugin name
+ _check_plugin_name(name)
+
+ -- do install
+ local installname = name
+ if reponame then
+ installname = reponame .. "@" .. name
+ end
+ local argv = {"lua", "private.xrepo", "install", "--plugin"}
+ -- we need to pass the common options to the sub-process, e.g. -y, -v, -D
+ if option.get("yes") then
+ table.insert(argv, "-y")
end
- return urls
+ if option.get("verbose") then
+ table.insert(argv, "-v")
+ end
+ if option.get("diagnosis") then
+ table.insert(argv, "-D")
+ end
+ table.insert(argv, installname)
+ os.execv(os.programfile(), argv)
end
--- get manifest path
-function _manifest_path()
- return path.join(global.directory(), "plugins", "manifest.txt")
+-- install a single plugin from a source directory (as the given name, default to the directory name)
+function _install_from_local(dir, name)
+ assert(os.isdir(dir) and os.isfile(path.join(dir, "xmake.lua")), "plugin path(%s): ${bright}xmake.lua${clear} not found!", dir)
+ name = name or path.filename(path.absolute(dir))
+ local dstdir = _get_plugindir(name)
+ assert(not os.isdir(dstdir), "plugin(%s) already exists!", name)
+ os.vcp(dir, dstdir)
+ cprint("${color.success}install ${bright}%s${clear} ok!", name)
end
--- load manifest
-function _load_manifest()
- local manifest_path = _manifest_path()
- if os.isfile(manifest_path) then
- return io.load(manifest_path)
+-- install a single plugin from a git url or github shortcut, e.g. https://github.com/xmake-io/hello-world
+function _install_from_git(url)
+ local branch
+ if url:startswith("github:") then
+ local i = url:find("#", 1, true)
+ if i then
+ branch = url:sub(i + 1)
+ url = url:sub(1, i - 1)
+ end
+ url = git.asgiturl(url)
end
+ local name = (path.filename(url):gsub("%.git$", ""))
+ local tmpdir = os.tmpfile() .. ".dir"
+ git.clone(url, {verbose = option.get("verbose"), branch = branch, outputdir = tmpdir})
+ os.tryrm(path.join(tmpdir, ".git"))
+ _install_from_local(tmpdir, name)
+ os.tryrm(tmpdir)
end
--- save manifest
-function _save_manifest(manifest)
- io.save(_manifest_path(), manifest)
+-- install a single plugin
+function _install_one(name)
+ -- parse repo@plugin format
+ local i = name:find("@", 1, true)
+ if i and not name:find("[/\\:]") then
+ local reponame = name:sub(1, i - 1)
+ local pluginname = name:sub(i + 1)
+ _install_plugins_from_repo(pluginname, reponame)
+ return
+ end
+
+ -- github shortcut: github:user/repo or github:user/repo#branch
+ if name:startswith("github:") then
+ _install_from_git(name)
+ return
+ end
+
+ -- git url or local path
+ if git.asgiturl(name) then
+ _install_from_git(name)
+ return
+ elseif os.isdir(name) then
+ _install_from_local(name)
+ return
+ end
+
+ -- plain name: try to find it in repositories
+ _install_plugins_from_repo(name)
end
-- install plugins
function _install()
-
- -- enter environment
+ local names = assert(option.get("plugins"), "please specify the plugins to be installed!")
environment.enter()
+ for _, name in ipairs(names) do
+ _install_one(name)
+ end
+ environment.leave()
+end
- try
- {
- function ()
+-- remove the given installed plugin
+function _remove()
+ local names = assert(option.get("plugins"), "please specify the plugin name to be removed!")
+ assert(#names == 1, "please specify only one plugin name to be removed!")
+ local name = names[1]
+ local dir = _get_plugindir(name)
+ assert(os.isdir(dir), "plugin(%s) not found!", name)
+ os.rmdir(dir)
+ cprint("${color.success}remove ${bright}%s${clear} ok!", name)
+end
- -- do install
- local urls = _plugin_urls()
- local tmpdir = os.tmpfile() .. ".dir"
- local plugindir = path.join(global.directory(), "plugins")
- local installed_url
- for _, url in ipairs(urls) do
- cprint("installing plugins from %s ..", url)
- git.clone(url, {verbose = option.get("verbose"), outputdir = tmpdir})
- installed_url = url
- break
- end
- for _, filepath in ipairs(os.files(path.join(tmpdir, "*", "xmake.lua"))) do
- local srcdir = path.directory(filepath)
- local name = path.filename(srcdir)
- local dstdir = path.join(plugindir, name)
- assert(not os.isdir(dstdir), "plugin(%s) already exists!", name)
- os.vcp(srcdir, dstdir)
- cprint(" ${yellow}->${clear} %s", name)
- end
- os.tryrm(tmpdir)
+-- get the description of a plugin from its xmake.lua menu
+function _plugin_description(dir)
+ local filepath = path.join(dir, "xmake.lua")
+ if os.isfile(filepath) then
+ local content = io.readfile(filepath)
+ if content then
+ -- parse description from task or package scope
+ return content:match("description%s*=%s*\"(.-)\"") or content:match("set_description%s*%(\"(.-)\"%)")
+ end
+ end
+end
- -- save manifest
- if installed_url then
- local manifest = _load_manifest() or {}
- manifest.urls = manifest.urls or {}
- table.join2(manifest.urls, installed_url)
- _save_manifest(manifest)
- end
- cprint("${bright}all plugins have been installed in %s!", plugindir)
- end,
- catch
- {
- function (errors)
- raise(errors)
+-- collect plugins (each subdirectory containing xmake.lua) under the given root
+--
+-- built-in/installed plugins are flat (<root>/<name>), while plugins in a
+-- repository follow the packages layout (<root>/<first-letter>/<name>)
+function _collect_plugins(root, seen, nested)
+ local entries = {}
+ local pattern = nested and path.join(root, "*", "*") or path.join(root, "*")
+ for _, dir in ipairs(os.dirs(pattern) or {}) do
+ local name = path.filename(dir)
+ if os.isfile(path.join(dir, "xmake.lua")) and (not seen or not seen[name]) then
+ if seen then
+ seen[name] = true
end
- }
- }
+ table.insert(entries, {name = name, description = _plugin_description(dir)})
+ end
+ end
+ return entries
+end
- -- leave environment
- environment.leave()
+-- print a plugin entry with its description aligned on the right
+function _print_plugin(name, description, width, note)
+ local suffix = description or ""
+ if note then
+ suffix = suffix ~= "" and (suffix .. " " .. note) or note
+ end
+ if suffix ~= "" then
+ local padding = math.max(width - #name, 1)
+ cprint(" ${color.dump.string}%s${clear}%s%s", name, (" "):rep(padding), suffix)
+ else
+ cprint(" ${color.dump.string}%s${clear}", name)
+ end
end
--- update plugins
-function _update()
+-- list all plugins
+function _list()
+ local seen = {}
- -- enter environment
- environment.enter()
+ -- collect plugins from every source
+ local builtin = _collect_plugins(path.join(os.programdir(), "plugins"), seen)
+ local plugindir = _get_plugindir()
+ local installed = _collect_plugins(plugindir, seen)
+ local avail = {}
+ for _, repo in ipairs(_repositories()) do
+ table.join2(avail, _collect_plugins(path.join(repo:directory(), "plugins"), seen, true))
+ end
- try
- {
- function ()
+ -- compute the alignment width from all plugin names
+ local width = 0
+ for _, entries in ipairs({builtin, installed, avail}) do
+ for _, entry in ipairs(entries) do
+ width = math.max(width, #entry.name + 4)
+ end
+ end
- -- do update
- local manifest = _load_manifest()
- assert(manifest and manifest.urls, "3rd plugins not found!")
- local urls = manifest.urls
- local plugindir = path.join(global.directory(), "plugins")
- for _, url in ipairs(urls) do
- cprint("updating plugins from %s ..", url)
- local tmpdir = os.tmpfile() .. ".dir"
- git.clone(url, {verbose = option.get("verbose"), outputdir = tmpdir})
- for _, filepath in ipairs(os.files(path.join(tmpdir, "*", "xmake.lua"))) do
- local srcdir = path.directory(filepath)
- local name = path.filename(srcdir)
- local dstdir = path.join(plugindir, name)
- os.tryrm(dstdir)
- os.vcp(srcdir, dstdir)
- cprint(" ${yellow}->${clear} %s", name)
- end
- os.tryrm(tmpdir)
- end
- cprint("${bright}all plugins have been updated in %s!", plugindir)
- end,
- catch
- {
- function (errors)
- raise(errors)
- end
- }
- }
+ -- built-in plugins
+ cprint("${bright}the built-in plugins:${clear}")
+ if #builtin > 0 then
+ for _, entry in ipairs(builtin) do
+ _print_plugin(entry.name, entry.description, width)
+ end
+ else
+ print(" (none)")
+ end
- -- leave environment
- environment.leave()
+ -- installed plugins
+ cprint("${bright}the installed plugins:${clear}")
+ if #installed > 0 then
+ for _, entry in ipairs(installed) do
+ _print_plugin(entry.name, entry.description, width)
+ end
+ else
+ print(" (none)")
+ end
+
+ -- plugins available in repositories (not yet installed)
+ cprint("${bright}available in configured repositories:${clear}")
+ if #avail > 0 then
+ for _, entry in ipairs(avail) do
+ local note = string.format("(run xmake plugin --install %s to install)", entry.name)
+ _print_plugin(entry.name, entry.description, width, note)
+ end
+ else
+ print(" (none)")
+ end
end
-- clear all installed plugins
function _clear()
- local plugindir = path.join(global.directory(), "plugins")
+ local plugindir = _get_plugindir()
if os.isdir(plugindir) then
os.rmdir(plugindir)
end
@@ -168,12 +257,14 @@ function _clear()
end
function main()
+ cprint("${bright color.warning}${text.warning}: ${color.warning}`xmake plugin` is deprecated, please use `xmake addon` instead!")
if option.get("install") then
_install()
- elseif option.get("update") then
- _update()
+ elseif option.get("remove") then
+ _remove()
+ elseif option.get("list") then
+ _list()
elseif option.get("clear") then
_clear()
end
end
-
diff --git a/xmake/plugins/plugin/xmake.lua b/xmake/plugins/plugin/xmake.lua
index 05d9e3631..1136b0044 100644
--- a/xmake/plugins/plugin/xmake.lua
+++ b/xmake/plugins/plugin/xmake.lua
@@ -23,14 +23,21 @@ task("plugin")
on_run("main")
set_menu {
usage = "xmake plugin [options]",
- description = "Manage plugins of xmake.",
+ description = "Manage plugins of xmake. (deprecated, please use `xmake addon` instead)",
options = {
{'i', "install", "k", nil, "Install plugins."},
- {'u', "update", "k", nil, "Update plugins."},
+ {'r', "remove", "k", nil, "Remove the given installed plugin."},
+ {'l', "list", "k", nil, "List all installed plugins."},
{'c', "clear", "k", nil, "Clear all installed plugins."},
- {nil, "plugins", "v", nil, "The plugins path or url.",
+ {nil, "plugins", "vs", nil, "The plugin paths, urls or names.",
"e.g.",
- " $ xmake plugin --install https://github.com/xmake-io/xmake-plugins",
- " $ xmake plugin --update"}
+ " $ xmake plugin --install https://github.com/myrepo/hello-world",
+ " $ xmake plugin --install github:myrepo/hello-world",
+ " $ xmake plugin --install github:myrepo/hello-world#dev",
+ " $ xmake plugin --install /tmp/my-plugin",
+ " $ xmake plugin --install xmake-repo@hello-world",
+ " $ xmake plugin --install hello-world",
+ " $ xmake plugin --remove hello-world",
+ " $ xmake plugin --list"}
}
}
diff --git a/xmake/plugins/project/clang/compile_commands.lua b/xmake/plugins/project/clang/compile_commands.lua
index 2f25194cc..a8fdb547f 100644
--- a/xmake/plugins/project/clang/compile_commands.lua
+++ b/xmake/plugins/project/clang/compile_commands.lua
@@ -69,10 +69,13 @@ end
-- specify windows sdk verison
function _get_windows_sdk_arguments(target)
local args = {}
- local msvc = target:toolchain("msvc")
- if msvc then
- local envs = msvc:runenvs()
- if envs then
+ if target and target:is_plat("windows") then
+ local toolchain = target:toolchain("msvc") or
+ target:toolchain("clang-cl") or
+ target:toolchain("clang") or
+ target:toolchain("llvm")
+ local envs = toolchain and toolchain:runenvs()
+ if envs and envs.INCLUDE then
for _, dir in ipairs(path.splitenv(envs.INCLUDE)) do
table.insert(args, "-imsvc")
table.insert(args, dir)
diff --git a/xmake/plugins/project/ninja/build_ninja.lua b/xmake/plugins/project/ninja/build_ninja.lua
index f89915e02..bd9a3d292 100644
--- a/xmake/plugins/project/ninja/build_ninja.lua
+++ b/xmake/plugins/project/ninja/build_ninja.lua
@@ -37,7 +37,8 @@ function _sourcebatch_is_built(sourcebatch)
local rulename = sourcebatch.rulename
if rulename == "c.build" or rulename == "c++.build"
or rulename == "asm.build" or rulename == "cuda.build"
- or rulename == "objc.build" or rulename == "objc++.build" then
+ or rulename == "objc.build" or rulename == "objc++.build"
+ or rulename == "win.sdk.resource" then
return true
end
end
diff --git a/xmake/plugins/project/utils/target_cmds.lua b/xmake/plugins/project/utils/target_cmds.lua
index 7ea3a13a0..1c5fed7c8 100644
--- a/xmake/plugins/project/utils/target_cmds.lua
+++ b/xmake/plugins/project/utils/target_cmds.lua
@@ -87,6 +87,9 @@ function get_target_buildcmds(target, opt)
cmd.program = os.programfile()
cmd.argv = table.join("lua", cmd.script, cmd.argv)
cmd.script = nil
+ elseif cmd.func and kind == "call" then
+ local name = cmd.opt and cmd.opt.name or "unknown"
+ wprint("%s: batchcmds:call() is not supported by the project generator and will be ignored!", name)
end
end
return buildcmds:cmds()
diff --git a/xmake/rules/c++/modules/builder.lua b/xmake/rules/c++/modules/builder.lua
index 6d82cca0b..b988ff152 100644
--- a/xmake/rules/c++/modules/builder.lua
+++ b/xmake/rules/c++/modules/builder.lua
@@ -85,6 +85,23 @@ function _get_buildfilejob_for(target, sourcefile, opt)
return _get_module_buildfilejob_for(target, sourcefile, opt.moduletype)
end
+-- merge jobdeps, we cannot use table.join2 directly,
+-- because it will override the previous deps with the same job name
+--
+-- e.g. a consumer job may depend on several reused bmi jobs from the same dep target
+-- @see https://github.com/xmake-io/xmake/issues/7690
+function _merge_jobdeps(jobdeps, deps)
+ for jobname, depnames in pairs(deps) do
+ local orderdeps = jobdeps[jobname]
+ if orderdeps then
+ table.join2(orderdeps, depnames)
+ else
+ jobdeps[jobname] = table.wrap(depnames)
+ end
+ end
+ return jobdeps
+end
+
function _get_jobdeps(target, module, jobgraph, buildfilejob)
local memcache = support.memcache()
@@ -177,6 +194,10 @@ function should_build(target, module)
local dependinfo = {}
dependinfo.files = {module.sourcefile}
dependinfo.values = {compinst:program(), compflags}
+ if module.bmifile and not module.headerunit and support.has_two_phase_compilation_support(target) then
+ local bmi_mode = support.has_precompile_reduced_bmi_support(target) and support.get_modulesprecompilereducedbmiflag(target) or "--precompile"
+ table.insert(dependinfo.values, bmi_mode)
+ end
local objectfile_exists = (module.headerunit or support.is_bmionly(target, module.sourcefile)) and true or os.isfile(module.objectfile)
dependinfo.lastmtime = (os.isfile(module.bmifile or module.objectfile) and objectfile_exists) and os.mtime(dependfile) or 0
@@ -224,6 +245,7 @@ function build_modules_for_jobgraph(target, jobgraph, built_modules)
profiler.enter(target:fullname(), "c++ modules", "builder", "schedule module bmi build jobs")
local builder = _builder(target)
local has_two_phase_compilation_support = support.has_two_phase_compilation_support(target)
+ local has_precompile_reduced_bmi = support.has_precompile_reduced_bmi_support(target)
local jobdeps = {}
local buildfilejobs = {}
@@ -236,11 +258,11 @@ function build_modules_for_jobgraph(target, jobgraph, built_modules)
else
if not support.is_bmionly(target, sourcefile) then
local buildfilejob = _get_module_buildfilejob_for(target, sourcefile, "objectfile")
- table.join2(jobdeps, _get_jobdeps(target, module, jobgraph, buildfilejob))
+ _merge_jobdeps(jobdeps, _get_jobdeps(target, module, jobgraph, buildfilejob))
table.insert(buildfilejobs, buildfilejob)
local objbuildfilejob = target:fullname() .. "/obj/" .. sourcefile
if jobgraph:has(objbuildfilejob) then
- jobdeps[objbuildfilejob] = {buildfilejob}
+ _merge_jobdeps(jobdeps, {[objbuildfilejob] = {buildfilejob}})
end
end
end
@@ -255,26 +277,31 @@ function build_modules_for_jobgraph(target, jobgraph, built_modules)
local buildfilejob = _get_module_buildfilejob_for(target, sourcefile, moduletype)
table.insert(buildfilejobs, buildfilejob)
jobgraph:add(buildfilejob, function(_, _, jobopt)
- progress.set_target(jobopt.progress, target)
+ local moduleopt = table.clone(jobopt)
+ progress.set_target(moduleopt.progress, target)
-- build bmi if named job
- jobopt.bmi = module.name
+ moduleopt.bmi = module.name
-- build objectfile here if two phase compilation is not supported
- jobopt.objectfile = not has_two_phase_compilation_support and not bmionly
- builder.make_module_job(target, module, jobopt)
+ moduleopt.objectfile = not has_two_phase_compilation_support and not bmionly
+ builder.make_module_job(target, module, moduleopt)
end)
- table.join2(jobdeps, _get_jobdeps(target, module, jobgraph, buildfilejob))
+ _merge_jobdeps(jobdeps, _get_jobdeps(target, module, jobgraph, buildfilejob))
-- if two phase compilation supported set jobdeps for objectfile job
if has_two_phase_compilation_support and not bmionly then
local objbuildfilejob = _get_module_buildfilejob_for(target, sourcefile, "objectfile")
- jobdeps[objbuildfilejob] = {buildfilejob}
+ if has_precompile_reduced_bmi then
+ _merge_jobdeps(jobdeps, _get_jobdeps(target, module, jobgraph, objbuildfilejob))
+ else
+ _merge_jobdeps(jobdeps, {[objbuildfilejob] = {buildfilejob}})
+ end
end
end
end)
-- insert saved jobdeps
for _, buildfilejob in ipairs(buildfilejobs) do
- table.join2(jobdeps, _get_saved_jobdeps_for(jobgraph, buildfilejob))
+ _merge_jobdeps(jobdeps, _get_saved_jobdeps_for(jobgraph, buildfilejob))
end
-- apply jobdeps
@@ -310,10 +337,11 @@ function build_objectfiles_for_jobgraph(target, jobgraph, built_modules)
local module = mapper.get(target, sourcefile)
local buildfilejob = _get_module_buildfilejob_for(target, sourcefile, "objectfile")
jobgraph:add(buildfilejob, function(_, _, jobopt)
- progress.set_target(jobopt.progress, target)
- jobopt.bmi = false
- jobopt.objectfile = true
- builder.make_module_job(target, module, jobopt)
+ local moduleopt = table.clone(jobopt)
+ progress.set_target(moduleopt.progress, target)
+ moduleopt.bmi = false
+ moduleopt.objectfile = true
+ builder.make_module_job(target, module, moduleopt)
end)
end
end
@@ -702,6 +730,16 @@ function build_bmis(target, jobgraph, _, opt)
local built_modules, built_headerunits, _ = scanner.sort_modules_by_dependencies(target, modules, {jobgraph = target:policy("build.jobgraph")})
local headerunits, stlheaderunits = scanner.sort_headerunits(target, built_headerunits)
if jobgraph.add_orders then -- jobgraph
+ if support.has_precompile_reduced_bmi_support(target) then
+ -- schedule object jobs in the BMI stage so both jobs can run concurrently
+ -- after their imported BMIs are ready
+ local append_requires_flags = _builder(target).append_requires_flags
+ if append_requires_flags then
+ append_requires_flags(target, built_modules)
+ end
+ build_objectfiles_for_jobgraph(target, jobgraph, built_modules)
+ end
+
-- build headerunits
if stlheaderunits or headerunits then
build_headerunits_for_jobgraph(target, jobgraph, stlheaderunits, headerunits)
@@ -743,6 +781,10 @@ function build_objectfiles(target, jobgraph, _, opt)
if target:is_moduleonly() and not target:data("cxx.modules.reused") or target:is_phony() then
return
end
+ if jobgraph.add_orders and support.has_precompile_reduced_bmi_support(target) then
+ -- object jobs have already been scheduled by build_bmis()
+ return
+ end
profiler.enter(target:fullname(), "c++ modules", "builder", "objectfiles")
local modules = scanner.get_modules(target)
-- avoid building non referenced modules
diff --git a/xmake/rules/c++/modules/clang/builder.lua b/xmake/rules/c++/modules/clang/builder.lua
index 4839733e0..4ecafb17e 100644
--- a/xmake/rules/c++/modules/clang/builder.lua
+++ b/xmake/rules/c++/modules/clang/builder.lua
@@ -65,6 +65,8 @@ function _make_modulebuildflags(target, module, opt)
local modules_reduced_bmi_flag = support.get_modulesreducedbmiflag(target)
local has_two_phases = target:policy("build.c++.modules.two_phases")
+ local has_precompile_reduced_bmi = support.has_precompile_reduced_bmi_support(target)
+ local modules_precompile_reduced_bmi_flag = has_precompile_reduced_bmi and support.get_modulesprecompilereducedbmiflag(target)
local flags
if opt.bmi then
local module_outputflag = support.get_moduleoutputflag(target)
@@ -72,7 +74,7 @@ function _make_modulebuildflags(target, module, opt)
flags = {"-x", "c++-module"}
if not opt.objectfile then
- table.insert(flags, "--precompile")
+ table.insert(flags, modules_precompile_reduced_bmi_flag or "--precompile")
if target:has_tool("cxx", "clang_cl") then
table.join2(flags, "/clang:-o", "/clang:" .. module.bmifile)
end
@@ -92,7 +94,7 @@ function _make_modulebuildflags(target, module, opt)
end
else
flags = {}
- if not has_two_phases or not module.bmifile then
+ if (has_precompile_reduced_bmi and support.has_module_extension(module.sourcefile)) or not has_two_phases or not module.bmifile then
flags = {"-x", "c++"}
end
local std = (module.name == "std" or module.name == "std.compat")
@@ -171,7 +173,7 @@ function _compile(target, flags, module, opt)
opt = opt or {}
local sourcefile = module.sourcefile
- if not opt.bmi and opt.objectfile and module.bmifile then
+ if not support.has_precompile_reduced_bmi_support(target) and not opt.bmi and opt.objectfile and module.bmifile then
sourcefile = module.bmifile
end
local outputfile = ((opt.bmi and not opt.objectfile) or opt.headerunit) and module.bmifile or module.objectfile
diff --git a/xmake/rules/c++/modules/clang/scanner.lua b/xmake/rules/c++/modules/clang/scanner.lua
index d73c40621..f0023cb7c 100644
--- a/xmake/rules/c++/modules/clang/scanner.lua
+++ b/xmake/rules/c++/modules/clang/scanner.lua
@@ -59,7 +59,8 @@ function scan_dependency_for(target, sourcefile, rescan, opt)
if option.get("verbose") then
print(os.args(table.join(clangscandeps, dependency_flags)))
end
- local outdata, errdata = os.iorunv(clangscandeps, dependency_flags)
+ local outdata, errdata = os.iorunv(clangscandeps, dependency_flags,
+ {envs = compinst:runenvs()})
assert(outdata, errdata)
io.writefile(jsonfile, outdata)
@@ -77,7 +78,7 @@ function scan_dependency_for(target, sourcefile, rescan, opt)
end)
local ifile = path.translate(path.join(outputdir, path.filename(file) .. ".i"))
compflags = table.join(compflags or {}, keepsystemincludesflag or {}, {"-E", "-x", "c++", file, "-o", ifile})
- os.vrunv(compinst:program(), compflags)
+ os.vrunv(compinst:program(), compflags, {envs = compinst:runenvs()})
local content = io.readfile(ifile)
os.rm(ifile)
return content
diff --git a/xmake/rules/c++/modules/clang/support.lua b/xmake/rules/c++/modules/clang/support.lua
index b1699d21a..a010fdde1 100644
--- a/xmake/rules/c++/modules/clang/support.lua
+++ b/xmake/rules/c++/modules/clang/support.lua
@@ -46,7 +46,10 @@ function _get_toolchain_includedirs_for_stlheaders(target, includedirs, clang)
table.insert(argv, 1, "-stdlib=libstdc++")
end
end
- local result = try {function () return os.iorunv(clang, argv, {envs = compinst:runenvs()}) end}
+ local compinst = target:compiler("cxx")
+ local result = try {function ()
+ return os.iorunv(clang, argv, {envs = compinst and compinst:runenvs()})
+ end}
if result then
for _, line in ipairs(result:split("\n", {plain = true})) do
local line = line:trim()
@@ -64,17 +67,25 @@ end
function _get_std_module_manifest_path(target)
local print_module_manifest_flag = get_print_library_module_manifest_path_flag(target)
- local clang_path = path.directory(get_clang_path(target))
+ local clang_path = get_clang_path(target)
+ if not clang_path then
+ return
+ end
+ local clang_dir = path.directory(clang_path)
if print_module_manifest_flag then
local compinst = target:compiler("cxx")
- local outdata, _ = try { function() return os.iorunv(compinst:program(), {"-std=c++23", "-stdlib=libc++", "--sysroot=" .. path.join(clang_path, ".."), print_module_manifest_flag}, {envs = compinst:runenvs()}) end }
+ local sysroot = "--sysroot=" .. path.join(clang_dir, "..")
+ local flags = {"-std=c++23", "-stdlib=libc++", sysroot, print_module_manifest_flag}
+ local outdata, _ = try {function()
+ return os.iorunv(compinst:program(), flags, {envs = compinst:runenvs()})
+ end}
if outdata and not outdata:startswith("<NOT PRESENT>") then
return outdata:trim()
end
end
-- fallback on custom detection
-- manifest can be found in <llvm_path>/lib subdirectory (i.e on debian it should be <llvm_path>/lib/x86_64-unknown-linux-gnu/)
- local clang_lib_path = path.join(clang_path, "..", "lib")
+ local clang_lib_path = path.join(clang_dir, "..", "lib")
local modules_json_path = path.join(clang_lib_path, "libc++.modules.json")
if not os.isfile(modules_json_path) then
modules_json_path = find_file("*/libc++.modules.json", clang_lib_path)
@@ -119,6 +130,13 @@ function has_two_phase_compilation_support(target)
return target:policy("build.c++.modules.two_phases")
end
+function has_precompile_reduced_bmi_support(target)
+ if not target:policy("build.c++.modules.clang.precompile_reduced_bmi") or not has_two_phase_compilation_support(target) then
+ return false
+ end
+ return get_modulesprecompilereducedbmiflag(target) ~= nil
+end
+
-- flags that doesn't affect bmi generation
function strippeable_flags()
-- speculative list as there is no resource that list flags that prevent reusability, this list will likely be improve over time
@@ -161,7 +179,11 @@ function toolchain_includedirs(target)
runtime_flag = "-stdlib=libstdc++"
end
end
- local _, result = try {function () return os.iorunv(clang, table.join({"-E", "-Wp,-v", "-xc++", os.nuldev()}, runtime_flag or {})) end}
+ local compinst = target:compiler("cxx")
+ local flags = table.join({"-E", "-Wp,-v", "-xc++", os.nuldev()}, runtime_flag or {})
+ local _, result = try {function ()
+ return os.iorunv(clang, flags, {envs = compinst and compinst:runenvs()})
+ end}
if result then
for _, line in ipairs(result:split("\n", {plain = true})) do
local line = line:trim()
@@ -183,8 +205,10 @@ function get_clang_path(target)
if not clang_path then
local program, toolname = target:tool("cxx")
if program and toolname:startswith("clang") then
+ local compinst = target:compiler("cxx")
+ local envs = compinst and compinst:runenvs() or os.getenvs()
local clang = find_tool(toolname, {program = program,
- envs = os.getenvs(), cachekey = "modules_support_clang_" .. toolname})
+ envs = envs, cachekey = "modules_support_clang_" .. toolname})
if clang then
clang_path = clang.program
end
@@ -201,8 +225,10 @@ function get_clang_version(target)
if not clang_version then
local program, toolname = target:tool("cxx")
if program and toolname:startswith("clang") then
+ local compinst = target:compiler("cxx")
+ local envs = compinst and compinst:runenvs() or os.getenvs()
local clang = find_tool(toolname, {program = program, version = true,
- envs = os.getenvs(), cachekey = "modules_support_clang_" .. toolname})
+ envs = envs, cachekey = "modules_support_clang_" .. toolname})
if clang then
clang_version = clang.version
end
@@ -229,10 +255,13 @@ function get_clang_scan_deps(target)
if dir and dir ~= "." and os.isdir(dir) then
program = path.join(dir, program)
end
- local result = find_tool("clang-scan-deps", {program = program, version = true})
+ local compinst = target:compiler("cxx")
+ local envs = compinst and compinst:runenvs() or os.getenvs()
+ local result = find_tool("clang-scan-deps",
+ {program = program, version = true, envs = envs})
if not result then
-- find a system wide alternative
- result = find_tool("clang-scan-deps", {version = true})
+ result = find_tool("clang-scan-deps", {version = true, envs = envs})
end
if result then
clang_scan_deps = result.program
@@ -295,9 +324,16 @@ function get_stdmodules(target)
return {path.normalize(path.join(try_std_module_directory, "std.cppm")), path.normalize(path.join(try_std_module_directory, "std.compat.cppm"))}
end
-- then try the directory relative to clang bin directory
- try_std_module_directory = path.join(path.directory(get_original_file(get_clang_path(target))), std_module_directory)
- if os.isdir(try_std_module_directory) then
- return {path.normalize(path.join(try_std_module_directory, "std.cppm")), path.normalize(path.join(try_std_module_directory, "std.compat.cppm"))}
+ local clang_path = get_clang_path(target)
+ if clang_path then
+ local clang_dir = path.directory(get_original_file(clang_path))
+ try_std_module_directory = path.join(clang_dir, std_module_directory)
+ if os.isdir(try_std_module_directory) then
+ return {
+ path.normalize(path.join(try_std_module_directory, "std.cppm")),
+ path.normalize(path.join(try_std_module_directory, "std.compat.cppm"))
+ }
+ end
end
elseif cpplib == "stdc++" then
-- dont be greedy and don't enable stdc++ std module support for llvm < 19
@@ -382,6 +418,25 @@ function get_modulesreducedbmiflag(target)
return modulesreducedbmiflag or nil
end
+function get_modulesprecompilereducedbmiflag(target)
+ local modulesprecompilereducedbmiflag = _g.modulesprecompilereducedbmiflag
+ if modulesprecompilereducedbmiflag == nil then
+ local compinst = target:compiler("cxx")
+ local checkflags = "--precompile-reduced-bmi"
+ if target:has_tool("cxx", "clang_cl") then
+ -- clang_cl.has_flags() appends -c and treats unused-argument warnings as errors,
+ -- but --precompile-reduced-bmi does not use -c. Ignore that specific warning and
+ -- treat unknown-argument warnings as errors to reject unsupported flags.
+ checkflags = {checkflags, "-Wno-unused-command-line-argument", "-Werror=unknown-argument"}
+ end
+ if compinst:has_flags(checkflags, "cxxflags", {flagskey = "clang_modules_precompile_reduced_bmi", tryrun = true}) then
+ modulesprecompilereducedbmiflag = "--precompile-reduced-bmi"
+ end
+ _g.modulesprecompilereducedbmiflag = modulesprecompilereducedbmiflag or false
+ end
+ return modulesprecompilereducedbmiflag or nil
+end
+
function has_clangscandepssupport(target)
local support_clangscandeps = _g.support_clangscandeps
if support_clangscandeps == nil then
diff --git a/xmake/rules/c++/modules/gcc/support.lua b/xmake/rules/c++/modules/gcc/support.lua
index 1204d207d..e57bd5a63 100644
--- a/xmake/rules/c++/modules/gcc/support.lua
+++ b/xmake/rules/c++/modules/gcc/support.lua
@@ -79,6 +79,13 @@ function has_two_phase_compilation_support(_)
return false
end
+function has_precompile_reduced_bmi_support(_)
+ return false
+end
+
+function get_modulesprecompilereducedbmiflag(_)
+end
+
-- flags that doesn't affect bmi generation
function strippeable_flags()
-- speculative list as there is no resource that list flags that prevent reusability, this list will likely be improve over time
diff --git a/xmake/rules/c++/modules/msvc/support.lua b/xmake/rules/c++/modules/msvc/support.lua
index 588921d0e..caaeb9cc6 100644
--- a/xmake/rules/c++/modules/msvc/support.lua
+++ b/xmake/rules/c++/modules/msvc/support.lua
@@ -120,6 +120,13 @@ function has_two_phase_compilation_support(_)
return false
end
+function has_precompile_reduced_bmi_support(_)
+ return false
+end
+
+function get_modulesprecompilereducedbmiflag(_)
+end
+
-- build c++23 standard modules if needed
function get_stdmodules(target, opt)
opt = opt or {}
diff --git a/xmake/rules/c++/modules/support.lua b/xmake/rules/c++/modules/support.lua
index 126e25c61..78d74ae6c 100644
--- a/xmake/rules/c++/modules/support.lua
+++ b/xmake/rules/c++/modules/support.lua
@@ -92,7 +92,15 @@ function get_cpplibrary_name(target)
end
elseif target:is_plat("macosx", "iphoneos", "watchos", "appletvos", "applexros", "bsd", "harmony") then
return "c++"
- elseif target:is_plat("linux", "mingw", "cygwin", "msys", "haiku") then
+ elseif target:is_plat("linux", "cygwin", "msys", "haiku") then
+ return "stdc++"
+ elseif target:is_plat("mingw") then
+ local toolchain_inst = target:toolchain("mingw")
+ local is_clang = (toolchain_inst and toolchain_inst:config("clang")) or
+ target:has_tool("cxx", "clang", "clangxx", "clang_cl")
+ if is_clang then
+ return "c++"
+ end
return "stdc++"
elseif target:is_plat("windows") then
return "msstl"
@@ -103,6 +111,14 @@ function has_two_phase_compilation_support(target)
return _support(target).has_two_phase_compilation_support(target)
end
+function has_precompile_reduced_bmi_support(target)
+ return _support(target).has_precompile_reduced_bmi_support(target)
+end
+
+function get_modulesprecompilereducedbmiflag(target)
+ return _support(target).get_modulesprecompilereducedbmiflag(target)
+end
+
-- strip flags not relevent for module reuse
function strip_flags(target, flags, opt)
@@ -429,4 +445,3 @@ function add_installfiles_for_modules(target, modules)
end
end
end
-
diff --git a/xmake/rules/utils/bin2c/utils.lua b/xmake/rules/utils/bin2c/utils.lua
index 405c2ed62..156752576 100644
--- a/xmake/rules/utils/bin2c/utils.lua
+++ b/xmake/rules/utils/bin2c/utils.lua
@@ -36,6 +36,7 @@ function generate_headerfile(target, batchcmds, binaryfile, opt)
opt = opt or {}
local rulename = opt.rulename or "utils.bin2c"
local progress = opt.progress
+ local fileconfig = target:fileconfig(binaryfile)
-- get header directory and file
local headerdir = opt.headerdir
@@ -52,6 +53,16 @@ function generate_headerfile(target, batchcmds, binaryfile, opt)
-- add includedirs
target:add("includedirs", headerdir)
+ -- transform binary data first
+ -- @see https://github.com/xmake-io/xmake/issues/7513
+ local transform = opt.transform or (fileconfig and fileconfig.transform) or target:extraconf("rules", rulename, "transform")
+ if transform then
+ batchcmds:show_progress(progress, "${color.build.object}transforming.bin2c %s", binaryfile)
+ local transformed_file = target:autogenfile(binaryfile)
+ batchcmds:call(transform, {binaryfile, transformed_file}, {name = "bin2c/transform", target = target})
+ binaryfile = transformed_file
+ end
+
-- add commands
if progress then
batchcmds:show_progress(progress, "${color.build.object}generating.bin2c %s", binaryfile)
@@ -69,7 +80,6 @@ function generate_headerfile(target, batchcmds, binaryfile, opt)
end
-- get nozeroend/zeroend (check file-level config first, then rule-level config, then opt)
- local fileconfig = target:fileconfig(binaryfile)
local nozeroend = nil
if fileconfig then
if fileconfig.nozeroend ~= nil then
diff --git a/xmake/rules/utils/bin2c/xmake.lua b/xmake/rules/utils/bin2c/xmake.lua
index 9f6338758..0d2745b60 100644
--- a/xmake/rules/utils/bin2c/xmake.lua
+++ b/xmake/rules/utils/bin2c/xmake.lua
@@ -33,7 +33,7 @@ rule("utils.bin2c")
-- generate header file
local headerfile = bin2c_utils.generate_headerfile(target, batchcmds, sourcefile_bin, {
- progress = opt.progress
+ progress = opt.progress,
})
-- add deps
diff --git a/xmake/rules/utils/bin2obj/utils.lua b/xmake/rules/utils/bin2obj/utils.lua
index 3e8802e97..dcd4b9c80 100644
--- a/xmake/rules/utils/bin2obj/utils.lua
+++ b/xmake/rules/utils/bin2obj/utils.lua
@@ -18,6 +18,21 @@
-- @file utils.lua
--
+-- pick a reference object already compiled by the target's own toolchain
+--
+-- bin2obj mirrors its class/endianness/machine/e_flags, so the generated object matches the
+-- toolchain exactly instead of guessing from the (sometimes ambiguous) arch name. we reuse one
+-- of the target's own objects rather than compiling a dedicated probe; any object the toolchain
+-- emits carries the same identity. we return the first one that already exists on disk, skipping
+-- the object we are generating right now (which does not exist yet anyway).
+function _get_refobj(target, objectfile)
+ for _, obj in ipairs(target:objectfiles()) do
+ if obj ~= objectfile and os.isfile(obj) then
+ return obj
+ end
+ end
+end
+
-- generate object file from binary file
--
-- @param target the target
@@ -35,6 +50,7 @@ function generate_objectfile(target, batchcmds, binaryfile, opt)
opt = opt or {}
local rulename = opt.rulename or "utils.bin2obj"
local progress = opt.progress
+ local fileconfig = target:fileconfig(binaryfile)
-- check for cosmocc toolchain
local is_cosmocc = target:toolchain("cosmocc") or (target:has_tool("cc", "cosmocc") and target:has_tool("ar", "cosmoar"))
@@ -53,6 +69,16 @@ function generate_objectfile(target, batchcmds, binaryfile, opt)
end
end
+ -- transform binary data first
+ -- @see https://github.com/xmake-io/xmake/issues/7513
+ local transform = opt.transform or (fileconfig and fileconfig.transform) or target:extraconf("rules", rulename, "transform")
+ if transform then
+ batchcmds:show_progress(progress, "${color.build.object}transforming.bin2obj %s", binaryfile)
+ local transformed_file = target:autogenfile(binaryfile)
+ batchcmds:call(transform, {binaryfile, transformed_file}, {name = "bin2obj/transform", target = target})
+ binaryfile = transformed_file
+ end
+
-- get object file
local objectfile = opt.objectfile
if not objectfile then
@@ -75,6 +101,9 @@ function generate_objectfile(target, batchcmds, binaryfile, opt)
-- get zeroend (default: false, but can be overridden)
local zeroend = opt.zeroend
if zeroend == nil then
+ zeroend = fileconfig and fileconfig.zeroend
+ end
+ if zeroend == nil then
zeroend = target:extraconf("rules", rulename, "zeroend") or false
end
@@ -116,6 +145,15 @@ function generate_objectfile(target, batchcmds, binaryfile, opt)
if is_cosmocc then
table.insert(argv, "--cosmocc")
end
+
+ -- mirror the toolchain's elf identity (endianness/machine/e_flags) from one of the target's
+ -- own compiled objects, so the output matches and links regardless of how the arch name maps
+ if format == "elf" and not is_cosmocc then
+ local refobj = _get_refobj(target, objectfile)
+ if refobj then
+ table.insert(argv, "--refobj=" .. refobj)
+ end
+ end
batchcmds:vlua("cli.binutils.bin2obj", argv)
return objectfile
diff --git a/xmake/rules/utils/bin2obj/xmake.lua b/xmake/rules/utils/bin2obj/xmake.lua
index 760b9d536..f461e67ef 100644
--- a/xmake/rules/utils/bin2obj/xmake.lua
+++ b/xmake/rules/utils/bin2obj/xmake.lua
@@ -21,18 +21,12 @@
rule("utils.bin2obj")
set_extensions(".bin")
add_orders("utils.bin2obj", "c++.build.modules.builder")
- on_buildcmd_file(function (target, batchcmds, sourcefile_bin, opt)
+ after_buildcmd_file(function (target, batchcmds, sourcefile_bin, opt)
import("rules.utils.bin2obj.utils", {alias = "bin2obj_utils", rootdir = os.programdir()})
- -- get zeroend (default: false)
- -- check file-level config first, then rule-level config
- local fileconfig = target:fileconfig(sourcefile_bin)
- local zeroend = (fileconfig and fileconfig.zeroend) or target:extraconf("rules", "utils.bin2obj", "zeroend") or false
-
-- convert binary file to object file
local objectfile = bin2obj_utils.generate_objectfile(target, batchcmds, sourcefile_bin, {
progress = opt.progress,
- zeroend = zeroend
})
-- add deps
diff --git a/xmake/rules/utils/glsl2spv/xmake.lua b/xmake/rules/utils/glsl2spv/xmake.lua
index 01abc561a..0374c2fe6 100644
--- a/xmake/rules/utils/glsl2spv/xmake.lua
+++ b/xmake/rules/utils/glsl2spv/xmake.lua
@@ -43,6 +43,7 @@
--
rule("utils.glsl2spv")
set_extensions(".vert", ".tesc", ".tese", ".geom", ".comp", ".frag", ".comp", ".mesh", ".task", ".rgen", ".rint", ".rahit", ".rchit", ".rmiss", ".rcall", ".glsl")
+ add_orders("utils.glsl2spv", "c++.build.modules.scanner")
on_load(function (target)
local is_bin2c = target:extraconf("rules", "utils.glsl2spv", "bin2c")
if is_bin2c then
@@ -53,7 +54,7 @@ rule("utils.glsl2spv")
target:add("includedirs", headerdir)
end
end)
- before_buildcmd_file(function (target, batchcmds, sourcefile_glsl, opt)
+ on_preparecmd_file(function (target, batchcmds, sourcefile_glsl, opt)
import("lib.detect.find_tool")
import("rules.utils.bin2obj.utils", {alias = "bin2obj_utils", rootdir = os.programdir()})
import("rules.utils.bin2c.utils", {alias = "bin2c_utils", rootdir = os.programdir()})
diff --git a/xmake/rules/utils/hlsl2spv/xmake.lua b/xmake/rules/utils/hlsl2spv/xmake.lua
index 750574eac..5eab8b25d 100644
--- a/xmake/rules/utils/hlsl2spv/xmake.lua
+++ b/xmake/rules/utils/hlsl2spv/xmake.lua
@@ -43,6 +43,7 @@
--
rule("utils.hlsl2spv")
set_extensions(".hlsl")
+ add_orders("utils.hlsl2spv", "c++.build.modules.scanner")
on_load(function (target)
local is_bin2c = target:extraconf("rules", "utils.hlsl2spv", "bin2c")
if is_bin2c then
@@ -54,7 +55,7 @@ rule("utils.hlsl2spv")
end
end)
- before_buildcmd_file(function (target, batchcmds, sourcefile_hlsl, opt)
+ on_preparecmd_file(function (target, batchcmds, sourcefile_hlsl, opt)
import("lib.detect.find_tool")
import("rules.utils.bin2obj.utils", {alias = "bin2obj_utils", rootdir = os.programdir()})
import("rules.utils.bin2c.utils", {alias = "bin2c_utils", rootdir = os.programdir()})
diff --git a/xmake/repository/templates/c++/console/src/main.cpp b/xmake/templates/c++/console/src/main.cpp
index 9b6abffea..9b6abffea 100644
--- a/xmake/repository/templates/c++/console/src/main.cpp
+++ b/xmake/templates/c++/console/src/main.cpp
diff --git a/xmake/repository/templates/c++/console/xmake.lua b/xmake/templates/c++/console/xmake.lua
index c73be8724..c73be8724 100644
--- a/xmake/repository/templates/c++/console/xmake.lua
+++ b/xmake/templates/c++/console/xmake.lua
diff --git a/xmake/repository/templates/c++/module/binary/.gitignore b/xmake/templates/c++/module/binary/.gitignore
index 152105761..152105761 100644
--- a/xmake/repository/templates/c++/module/binary/.gitignore
+++ b/xmake/templates/c++/module/binary/.gitignore
diff --git a/xmake/repository/templates/c++/module/binary/modules/binary/bar/.gitignore b/xmake/templates/c++/module/binary/modules/binary/bar/.gitignore
index 152105761..152105761 100644
--- a/xmake/repository/templates/c++/module/binary/modules/binary/bar/.gitignore
+++ b/xmake/templates/c++/module/binary/modules/binary/bar/.gitignore
diff --git a/xmake/repository/templates/c++/module/binary/modules/binary/bar/src/add.cpp b/xmake/templates/c++/module/binary/modules/binary/bar/src/add.cpp
index 93fdf0ad8..93fdf0ad8 100644
--- a/xmake/repository/templates/c++/module/binary/modules/binary/bar/src/add.cpp
+++ b/xmake/templates/c++/module/binary/modules/binary/bar/src/add.cpp
diff --git a/xmake/repository/templates/c++/module/binary/modules/binary/bar/src/sub.cpp b/xmake/templates/c++/module/binary/modules/binary/bar/src/sub.cpp
index ae7b6d1f2..ae7b6d1f2 100644
--- a/xmake/repository/templates/c++/module/binary/modules/binary/bar/src/sub.cpp
+++ b/xmake/templates/c++/module/binary/modules/binary/bar/src/sub.cpp
diff --git a/xmake/repository/templates/c++/module/binary/modules/binary/bar/xmake.lua b/xmake/templates/c++/module/binary/modules/binary/bar/xmake.lua
index add3c6e97..add3c6e97 100644
--- a/xmake/repository/templates/c++/module/binary/modules/binary/bar/xmake.lua
+++ b/xmake/templates/c++/module/binary/modules/binary/bar/xmake.lua
diff --git a/xmake/repository/templates/c++/module/binary/src/main.cpp b/xmake/templates/c++/module/binary/src/main.cpp
index 9b6abffea..9b6abffea 100644
--- a/xmake/repository/templates/c++/module/binary/src/main.cpp
+++ b/xmake/templates/c++/module/binary/src/main.cpp
diff --git a/xmake/repository/templates/c++/module/binary/xmake.lua b/xmake/templates/c++/module/binary/xmake.lua
index d73d457d3..d73d457d3 100644
--- a/xmake/repository/templates/c++/module/binary/xmake.lua
+++ b/xmake/templates/c++/module/binary/xmake.lua
diff --git a/xmake/repository/templates/c++/module/shared/.gitignore b/xmake/templates/c++/module/shared/.gitignore
index 152105761..152105761 100644
--- a/xmake/repository/templates/c++/module/shared/.gitignore
+++ b/xmake/templates/c++/module/shared/.gitignore
diff --git a/xmake/repository/templates/c++/module/shared/modules/shared/foo/src/foo.cpp b/xmake/templates/c++/module/shared/modules/shared/foo/src/foo.cpp
index adde214ca..adde214ca 100644
--- a/xmake/repository/templates/c++/module/shared/modules/shared/foo/src/foo.cpp
+++ b/xmake/templates/c++/module/shared/modules/shared/foo/src/foo.cpp
diff --git a/xmake/repository/templates/c++/module/shared/modules/shared/foo/xmake.lua b/xmake/templates/c++/module/shared/modules/shared/foo/xmake.lua
index 9b3423d41..9b3423d41 100644
--- a/xmake/repository/templates/c++/module/shared/modules/shared/foo/xmake.lua
+++ b/xmake/templates/c++/module/shared/modules/shared/foo/xmake.lua
diff --git a/xmake/repository/templates/c++/module/shared/src/main.cpp b/xmake/templates/c++/module/shared/src/main.cpp
index 9b6abffea..9b6abffea 100644
--- a/xmake/repository/templates/c++/module/shared/src/main.cpp
+++ b/xmake/templates/c++/module/shared/src/main.cpp
diff --git a/xmake/repository/templates/c++/module/shared/xmake.lua b/xmake/templates/c++/module/shared/xmake.lua
index 43aafe904..43aafe904 100644
--- a/xmake/repository/templates/c++/module/shared/xmake.lua
+++ b/xmake/templates/c++/module/shared/xmake.lua
diff --git a/xmake/repository/templates/c++/qt/console/src/main.cpp b/xmake/templates/c++/qt/console/src/main.cpp
index e876eea4f..e876eea4f 100644
--- a/xmake/repository/templates/c++/qt/console/src/main.cpp
+++ b/xmake/templates/c++/qt/console/src/main.cpp
diff --git a/xmake/repository/templates/c++/qt/console/xmake.lua b/xmake/templates/c++/qt/console/xmake.lua
index 2ce5aa993..2ce5aa993 100644
--- a/xmake/repository/templates/c++/qt/console/xmake.lua
+++ b/xmake/templates/c++/qt/console/xmake.lua
diff --git a/xmake/repository/templates/c++/qt/quickapp/src/main.cpp b/xmake/templates/c++/qt/quickapp/src/main.cpp
index 7d9cb80df..7d9cb80df 100644
--- a/xmake/repository/templates/c++/qt/quickapp/src/main.cpp
+++ b/xmake/templates/c++/qt/quickapp/src/main.cpp
diff --git a/xmake/repository/templates/c++/qt/quickapp/src/main.qml b/xmake/templates/c++/qt/quickapp/src/main.qml
index 120cdbddd..120cdbddd 100644
--- a/xmake/repository/templates/c++/qt/quickapp/src/main.qml
+++ b/xmake/templates/c++/qt/quickapp/src/main.qml
diff --git a/xmake/repository/templates/c++/qt/quickapp/src/qml.qrc b/xmake/templates/c++/qt/quickapp/src/qml.qrc
index 5f6483ac3..5f6483ac3 100644
--- a/xmake/repository/templates/c++/qt/quickapp/src/qml.qrc
+++ b/xmake/templates/c++/qt/quickapp/src/qml.qrc
diff --git a/xmake/repository/templates/c++/qt/quickapp/xmake.lua b/xmake/templates/c++/qt/quickapp/xmake.lua
index 3f71d1c15..3f71d1c15 100644
--- a/xmake/repository/templates/c++/qt/quickapp/xmake.lua
+++ b/xmake/templates/c++/qt/quickapp/xmake.lua
diff --git a/xmake/repository/templates/c++/qt/quickapp_static/src/main.cpp b/xmake/templates/c++/qt/quickapp_static/src/main.cpp
index 7d9cb80df..7d9cb80df 100644
--- a/xmake/repository/templates/c++/qt/quickapp_static/src/main.cpp
+++ b/xmake/templates/c++/qt/quickapp_static/src/main.cpp
diff --git a/xmake/repository/templates/c++/qt/quickapp_static/src/main.qml b/xmake/templates/c++/qt/quickapp_static/src/main.qml
index 120cdbddd..120cdbddd 100644
--- a/xmake/repository/templates/c++/qt/quickapp_static/src/main.qml
+++ b/xmake/templates/c++/qt/quickapp_static/src/main.qml
diff --git a/xmake/repository/templates/c++/qt/quickapp_static/src/qml.qrc b/xmake/templates/c++/qt/quickapp_static/src/qml.qrc
index 5f6483ac3..5f6483ac3 100644
--- a/xmake/repository/templates/c++/qt/quickapp_static/src/qml.qrc
+++ b/xmake/templates/c++/qt/quickapp_static/src/qml.qrc
diff --git a/xmake/repository/templates/c++/qt/quickapp_static/xmake.lua b/xmake/templates/c++/qt/quickapp_static/xmake.lua
index 0d311a762..0d311a762 100644
--- a/xmake/repository/templates/c++/qt/quickapp_static/xmake.lua
+++ b/xmake/templates/c++/qt/quickapp_static/xmake.lua
diff --git a/xmake/repository/templates/c++/qt/shared/src/demo.cpp b/xmake/templates/c++/qt/shared/src/demo.cpp
index 576c861f0..576c861f0 100644
--- a/xmake/repository/templates/c++/qt/shared/src/demo.cpp
+++ b/xmake/templates/c++/qt/shared/src/demo.cpp
diff --git a/xmake/repository/templates/c++/qt/shared/src/demo.h b/xmake/templates/c++/qt/shared/src/demo.h
index 89f29ec46..89f29ec46 100644
--- a/xmake/repository/templates/c++/qt/shared/src/demo.h
+++ b/xmake/templates/c++/qt/shared/src/demo.h
diff --git a/xmake/repository/templates/c++/qt/shared/src/demo_global.h b/xmake/templates/c++/qt/shared/src/demo_global.h
index d6b1ddf0b..d6b1ddf0b 100644
--- a/xmake/repository/templates/c++/qt/shared/src/demo_global.h
+++ b/xmake/templates/c++/qt/shared/src/demo_global.h
diff --git a/xmake/repository/templates/c++/qt/shared/xmake.lua b/xmake/templates/c++/qt/shared/xmake.lua
index 3b07153b9..3b07153b9 100644
--- a/xmake/repository/templates/c++/qt/shared/xmake.lua
+++ b/xmake/templates/c++/qt/shared/xmake.lua
diff --git a/xmake/repository/templates/c++/qt/static/src/demo.cpp b/xmake/templates/c++/qt/static/src/demo.cpp
index 576c861f0..576c861f0 100644
--- a/xmake/repository/templates/c++/qt/static/src/demo.cpp
+++ b/xmake/templates/c++/qt/static/src/demo.cpp
diff --git a/xmake/repository/templates/c++/qt/static/src/demo.h b/xmake/templates/c++/qt/static/src/demo.h
index 24730c9d8..24730c9d8 100644
--- a/xmake/repository/templates/c++/qt/static/src/demo.h
+++ b/xmake/templates/c++/qt/static/src/demo.h
diff --git a/xmake/repository/templates/c++/qt/static/xmake.lua b/xmake/templates/c++/qt/static/xmake.lua
index 371bdf082..371bdf082 100644
--- a/xmake/repository/templates/c++/qt/static/xmake.lua
+++ b/xmake/templates/c++/qt/static/xmake.lua
diff --git a/xmake/repository/templates/c++/qt/widgetapp/src/main.cpp b/xmake/templates/c++/qt/widgetapp/src/main.cpp
index 191ae18d2..191ae18d2 100644
--- a/xmake/repository/templates/c++/qt/widgetapp/src/main.cpp
+++ b/xmake/templates/c++/qt/widgetapp/src/main.cpp
diff --git a/xmake/repository/templates/c++/qt/widgetapp/src/mainwindow.cpp b/xmake/templates/c++/qt/widgetapp/src/mainwindow.cpp
index c9c6a0d98..c9c6a0d98 100644
--- a/xmake/repository/templates/c++/qt/widgetapp/src/mainwindow.cpp
+++ b/xmake/templates/c++/qt/widgetapp/src/mainwindow.cpp
diff --git a/xmake/repository/templates/c++/qt/widgetapp/src/mainwindow.h b/xmake/templates/c++/qt/widgetapp/src/mainwindow.h
index a4245aa42..a4245aa42 100644
--- a/xmake/repository/templates/c++/qt/widgetapp/src/mainwindow.h
+++ b/xmake/templates/c++/qt/widgetapp/src/mainwindow.h
diff --git a/xmake/repository/templates/c++/qt/widgetapp/src/mainwindow.ui b/xmake/templates/c++/qt/widgetapp/src/mainwindow.ui
index 1c59b695e..1c59b695e 100644
--- a/xmake/repository/templates/c++/qt/widgetapp/src/mainwindow.ui
+++ b/xmake/templates/c++/qt/widgetapp/src/mainwindow.ui
diff --git a/xmake/repository/templates/c++/qt/widgetapp/xmake.lua b/xmake/templates/c++/qt/widgetapp/xmake.lua
index e0f9f455e..e0f9f455e 100644
--- a/xmake/repository/templates/c++/qt/widgetapp/xmake.lua
+++ b/xmake/templates/c++/qt/widgetapp/xmake.lua
diff --git a/xmake/repository/templates/c++/qt/widgetapp_static/src/main.cpp b/xmake/templates/c++/qt/widgetapp_static/src/main.cpp
index db5b0cce8..db5b0cce8 100644
--- a/xmake/repository/templates/c++/qt/widgetapp_static/src/main.cpp
+++ b/xmake/templates/c++/qt/widgetapp_static/src/main.cpp
diff --git a/xmake/repository/templates/c++/qt/widgetapp_static/src/mainwindow.cpp b/xmake/templates/c++/qt/widgetapp_static/src/mainwindow.cpp
index c9c6a0d98..c9c6a0d98 100644
--- a/xmake/repository/templates/c++/qt/widgetapp_static/src/mainwindow.cpp
+++ b/xmake/templates/c++/qt/widgetapp_static/src/mainwindow.cpp
diff --git a/xmake/repository/templates/c++/qt/widgetapp_static/src/mainwindow.h b/xmake/templates/c++/qt/widgetapp_static/src/mainwindow.h
index a4245aa42..a4245aa42 100644
--- a/xmake/repository/templates/c++/qt/widgetapp_static/src/mainwindow.h
+++ b/xmake/templates/c++/qt/widgetapp_static/src/mainwindow.h
diff --git a/xmake/repository/templates/c++/qt/widgetapp_static/src/mainwindow.ui b/xmake/templates/c++/qt/widgetapp_static/src/mainwindow.ui
index 1c59b695e..1c59b695e 100644
--- a/xmake/repository/templates/c++/qt/widgetapp_static/src/mainwindow.ui
+++ b/xmake/templates/c++/qt/widgetapp_static/src/mainwindow.ui
diff --git a/xmake/repository/templates/c++/qt/widgetapp_static/xmake.lua b/xmake/templates/c++/qt/widgetapp_static/xmake.lua
index 2c357e531..2c357e531 100644
--- a/xmake/repository/templates/c++/qt/widgetapp_static/xmake.lua
+++ b/xmake/templates/c++/qt/widgetapp_static/xmake.lua
diff --git a/xmake/repository/templates/c++/shared/src/foo.cpp b/xmake/templates/c++/shared/src/foo.cpp
index 1a1fb3425..1a1fb3425 100644
--- a/xmake/repository/templates/c++/shared/src/foo.cpp
+++ b/xmake/templates/c++/shared/src/foo.cpp
diff --git a/xmake/repository/templates/c++/shared/src/foo.h b/xmake/templates/c++/shared/src/foo.h
index bd496c784..bd496c784 100644
--- a/xmake/repository/templates/c++/shared/src/foo.h
+++ b/xmake/templates/c++/shared/src/foo.h
diff --git a/xmake/repository/templates/c++/shared/src/main.cpp b/xmake/templates/c++/shared/src/main.cpp
index 3834ee4d3..3834ee4d3 100644
--- a/xmake/repository/templates/c++/shared/src/main.cpp
+++ b/xmake/templates/c++/shared/src/main.cpp
diff --git a/xmake/repository/templates/c++/shared/xmake.lua b/xmake/templates/c++/shared/xmake.lua
index 96bd36d7c..96bd36d7c 100644
--- a/xmake/repository/templates/c++/shared/xmake.lua
+++ b/xmake/templates/c++/shared/xmake.lua
diff --git a/xmake/repository/templates/c++/static/src/foo.cpp b/xmake/templates/c++/static/src/foo.cpp
index 1a1fb3425..1a1fb3425 100644
--- a/xmake/repository/templates/c++/static/src/foo.cpp
+++ b/xmake/templates/c++/static/src/foo.cpp
diff --git a/xmake/repository/templates/c++/static/src/foo.h b/xmake/templates/c++/static/src/foo.h
index d2506bca9..d2506bca9 100644
--- a/xmake/repository/templates/c++/static/src/foo.h
+++ b/xmake/templates/c++/static/src/foo.h
diff --git a/xmake/repository/templates/c++/static/src/main.cpp b/xmake/templates/c++/static/src/main.cpp
index 3834ee4d3..3834ee4d3 100644
--- a/xmake/repository/templates/c++/static/src/main.cpp
+++ b/xmake/templates/c++/static/src/main.cpp
diff --git a/xmake/repository/templates/c++/static/xmake.lua b/xmake/templates/c++/static/xmake.lua
index 2c15d36e8..2c15d36e8 100644
--- a/xmake/repository/templates/c++/static/xmake.lua
+++ b/xmake/templates/c++/static/xmake.lua
diff --git a/xmake/repository/templates/c++/wxwidgets/src/main.cpp b/xmake/templates/c++/wxwidgets/src/main.cpp
index 4b43fe6e3..4b43fe6e3 100644
--- a/xmake/repository/templates/c++/wxwidgets/src/main.cpp
+++ b/xmake/templates/c++/wxwidgets/src/main.cpp
diff --git a/xmake/repository/templates/c++/wxwidgets/xmake.lua b/xmake/templates/c++/wxwidgets/xmake.lua
index 65a05c72c..65a05c72c 100644
--- a/xmake/repository/templates/c++/wxwidgets/xmake.lua
+++ b/xmake/templates/c++/wxwidgets/xmake.lua
diff --git a/xmake/repository/templates/c++/xmake/cli/src/lni/main.cpp b/xmake/templates/c++/xmake/cli/src/lni/main.cpp
index 6dd5b81b1..6dd5b81b1 100644
--- a/xmake/repository/templates/c++/xmake/cli/src/lni/main.cpp
+++ b/xmake/templates/c++/xmake/cli/src/lni/main.cpp
diff --git a/xmake/repository/templates/c++/xmake/cli/src/lua/main.lua b/xmake/templates/c++/xmake/cli/src/lua/main.lua
index d87daf102..d87daf102 100644
--- a/xmake/repository/templates/c++/xmake/cli/src/lua/main.lua
+++ b/xmake/templates/c++/xmake/cli/src/lua/main.lua
diff --git a/xmake/repository/templates/c++/xmake/cli/xmake.lua b/xmake/templates/c++/xmake/cli/xmake.lua
index cf1987c60..cf1987c60 100644
--- a/xmake/repository/templates/c++/xmake/cli/xmake.lua
+++ b/xmake/templates/c++/xmake/cli/xmake.lua
diff --git a/xmake/repository/templates/c/console/src/main.c b/xmake/templates/c/console/src/main.c
index ccf4b2a77..ccf4b2a77 100644
--- a/xmake/repository/templates/c/console/src/main.c
+++ b/xmake/templates/c/console/src/main.c
diff --git a/xmake/repository/templates/c/console/xmake.lua b/xmake/templates/c/console/xmake.lua
index 7e9356f26..7e9356f26 100644
--- a/xmake/repository/templates/c/console/xmake.lua
+++ b/xmake/templates/c/console/xmake.lua
diff --git a/xmake/repository/templates/c/module/binary/.gitignore b/xmake/templates/c/module/binary/.gitignore
index 152105761..152105761 100644
--- a/xmake/repository/templates/c/module/binary/.gitignore
+++ b/xmake/templates/c/module/binary/.gitignore
diff --git a/xmake/repository/templates/c/module/binary/modules/binary/bar/.gitignore b/xmake/templates/c/module/binary/modules/binary/bar/.gitignore
index 152105761..152105761 100644
--- a/xmake/repository/templates/c/module/binary/modules/binary/bar/.gitignore
+++ b/xmake/templates/c/module/binary/modules/binary/bar/.gitignore
diff --git a/xmake/repository/templates/c/module/binary/modules/binary/bar/src/add.c b/xmake/templates/c/module/binary/modules/binary/bar/src/add.c
index 93fdf0ad8..93fdf0ad8 100644
--- a/xmake/repository/templates/c/module/binary/modules/binary/bar/src/add.c
+++ b/xmake/templates/c/module/binary/modules/binary/bar/src/add.c
diff --git a/xmake/repository/templates/c/module/binary/modules/binary/bar/src/sub.c b/xmake/templates/c/module/binary/modules/binary/bar/src/sub.c
index ae7b6d1f2..ae7b6d1f2 100644
--- a/xmake/repository/templates/c/module/binary/modules/binary/bar/src/sub.c
+++ b/xmake/templates/c/module/binary/modules/binary/bar/src/sub.c
diff --git a/xmake/repository/templates/c/module/binary/modules/binary/bar/xmake.lua b/xmake/templates/c/module/binary/modules/binary/bar/xmake.lua
index d4983d741..d4983d741 100644
--- a/xmake/repository/templates/c/module/binary/modules/binary/bar/xmake.lua
+++ b/xmake/templates/c/module/binary/modules/binary/bar/xmake.lua
diff --git a/xmake/repository/templates/c/module/binary/src/main.c b/xmake/templates/c/module/binary/src/main.c
index ccf4b2a77..ccf4b2a77 100644
--- a/xmake/repository/templates/c/module/binary/src/main.c
+++ b/xmake/templates/c/module/binary/src/main.c
diff --git a/xmake/repository/templates/c/module/binary/xmake.lua b/xmake/templates/c/module/binary/xmake.lua
index 7df556cb8..7df556cb8 100644
--- a/xmake/repository/templates/c/module/binary/xmake.lua
+++ b/xmake/templates/c/module/binary/xmake.lua
diff --git a/xmake/repository/templates/c/module/shared/.gitignore b/xmake/templates/c/module/shared/.gitignore
index 152105761..152105761 100644
--- a/xmake/repository/templates/c/module/shared/.gitignore
+++ b/xmake/templates/c/module/shared/.gitignore
diff --git a/xmake/repository/templates/c/module/shared/modules/shared/foo/src/foo.c b/xmake/templates/c/module/shared/modules/shared/foo/src/foo.c
index adde214ca..adde214ca 100644
--- a/xmake/repository/templates/c/module/shared/modules/shared/foo/src/foo.c
+++ b/xmake/templates/c/module/shared/modules/shared/foo/src/foo.c
diff --git a/xmake/repository/templates/c/module/shared/modules/shared/foo/xmake.lua b/xmake/templates/c/module/shared/modules/shared/foo/xmake.lua
index 035573d9f..035573d9f 100644
--- a/xmake/repository/templates/c/module/shared/modules/shared/foo/xmake.lua
+++ b/xmake/templates/c/module/shared/modules/shared/foo/xmake.lua
diff --git a/xmake/repository/templates/c/module/shared/src/main.c b/xmake/templates/c/module/shared/src/main.c
index ccf4b2a77..ccf4b2a77 100644
--- a/xmake/repository/templates/c/module/shared/src/main.c
+++ b/xmake/templates/c/module/shared/src/main.c
diff --git a/xmake/repository/templates/c/module/shared/xmake.lua b/xmake/templates/c/module/shared/xmake.lua
index 4b92b0b9e..4b92b0b9e 100644
--- a/xmake/repository/templates/c/module/shared/xmake.lua
+++ b/xmake/templates/c/module/shared/xmake.lua
diff --git a/xmake/repository/templates/c/shared/src/foo.c b/xmake/templates/c/shared/src/foo.c
index 1a1fb3425..1a1fb3425 100644
--- a/xmake/repository/templates/c/shared/src/foo.c
+++ b/xmake/templates/c/shared/src/foo.c
diff --git a/xmake/repository/templates/c/shared/src/foo.h b/xmake/templates/c/shared/src/foo.h
index e15925829..e15925829 100644
--- a/xmake/repository/templates/c/shared/src/foo.h
+++ b/xmake/templates/c/shared/src/foo.h
diff --git a/xmake/repository/templates/c/shared/src/main.c b/xmake/templates/c/shared/src/main.c
index f1c29d751..f1c29d751 100644
--- a/xmake/repository/templates/c/shared/src/main.c
+++ b/xmake/templates/c/shared/src/main.c
diff --git a/xmake/repository/templates/c/shared/xmake.lua b/xmake/templates/c/shared/xmake.lua
index 11e8dd0dd..11e8dd0dd 100644
--- a/xmake/repository/templates/c/shared/xmake.lua
+++ b/xmake/templates/c/shared/xmake.lua
diff --git a/xmake/repository/templates/c/static/src/foo.c b/xmake/templates/c/static/src/foo.c
index 1a1fb3425..1a1fb3425 100644
--- a/xmake/repository/templates/c/static/src/foo.c
+++ b/xmake/templates/c/static/src/foo.c
diff --git a/xmake/repository/templates/c/static/src/foo.h b/xmake/templates/c/static/src/foo.h
index 6295ab95c..6295ab95c 100644
--- a/xmake/repository/templates/c/static/src/foo.h
+++ b/xmake/templates/c/static/src/foo.h
diff --git a/xmake/repository/templates/c/static/src/main.c b/xmake/templates/c/static/src/main.c
index f1c29d751..f1c29d751 100644
--- a/xmake/repository/templates/c/static/src/main.c
+++ b/xmake/templates/c/static/src/main.c
diff --git a/xmake/repository/templates/c/static/xmake.lua b/xmake/templates/c/static/xmake.lua
index f00e9f4f7..f00e9f4f7 100644
--- a/xmake/repository/templates/c/static/xmake.lua
+++ b/xmake/templates/c/static/xmake.lua
diff --git a/xmake/repository/templates/c/xmake/cli/src/lni/main.c b/xmake/templates/c/xmake/cli/src/lni/main.c
index b1cef4e40..b1cef4e40 100644
--- a/xmake/repository/templates/c/xmake/cli/src/lni/main.c
+++ b/xmake/templates/c/xmake/cli/src/lni/main.c
diff --git a/xmake/repository/templates/c/xmake/cli/src/lua/main.lua b/xmake/templates/c/xmake/cli/src/lua/main.lua
index d87daf102..d87daf102 100644
--- a/xmake/repository/templates/c/xmake/cli/src/lua/main.lua
+++ b/xmake/templates/c/xmake/cli/src/lua/main.lua
diff --git a/xmake/repository/templates/c/xmake/cli/xmake.lua b/xmake/templates/c/xmake/cli/xmake.lua
index 33422b5c0..33422b5c0 100644
--- a/xmake/repository/templates/c/xmake/cli/xmake.lua
+++ b/xmake/templates/c/xmake/cli/xmake.lua
diff --git a/xmake/repository/templates/csharp/console/src/Program.cs b/xmake/templates/csharp/console/src/Program.cs
index 5f75209cb..5f75209cb 100644
--- a/xmake/repository/templates/csharp/console/src/Program.cs
+++ b/xmake/templates/csharp/console/src/Program.cs
diff --git a/xmake/repository/templates/csharp/console/xmake.lua b/xmake/templates/csharp/console/xmake.lua
index d19ce0fcd..d19ce0fcd 100644
--- a/xmake/repository/templates/csharp/console/xmake.lua
+++ b/xmake/templates/csharp/console/xmake.lua
diff --git a/xmake/repository/templates/csharp/shared/src/foo.cs b/xmake/templates/csharp/shared/src/foo.cs
index bba30d999..bba30d999 100644
--- a/xmake/repository/templates/csharp/shared/src/foo.cs
+++ b/xmake/templates/csharp/shared/src/foo.cs
diff --git a/xmake/repository/templates/csharp/shared/src/main.cs b/xmake/templates/csharp/shared/src/main.cs
index c2e4eb2fd..c2e4eb2fd 100644
--- a/xmake/repository/templates/csharp/shared/src/main.cs
+++ b/xmake/templates/csharp/shared/src/main.cs
diff --git a/xmake/repository/templates/csharp/shared/xmake.lua b/xmake/templates/csharp/shared/xmake.lua
index 256ed0f65..256ed0f65 100644
--- a/xmake/repository/templates/csharp/shared/xmake.lua
+++ b/xmake/templates/csharp/shared/xmake.lua
diff --git a/xmake/repository/templates/csharp/static/src/foo.cs b/xmake/templates/csharp/static/src/foo.cs
index bba30d999..bba30d999 100644
--- a/xmake/repository/templates/csharp/static/src/foo.cs
+++ b/xmake/templates/csharp/static/src/foo.cs
diff --git a/xmake/repository/templates/csharp/static/src/main.cs b/xmake/templates/csharp/static/src/main.cs
index c2e4eb2fd..c2e4eb2fd 100644
--- a/xmake/repository/templates/csharp/static/src/main.cs
+++ b/xmake/templates/csharp/static/src/main.cs
diff --git a/xmake/repository/templates/csharp/static/xmake.lua b/xmake/templates/csharp/static/xmake.lua
index 51bce92a0..51bce92a0 100644
--- a/xmake/repository/templates/csharp/static/xmake.lua
+++ b/xmake/templates/csharp/static/xmake.lua
diff --git a/xmake/repository/templates/cuda/console/src/main.cu b/xmake/templates/cuda/console/src/main.cu
index 2fe9ac797..2fe9ac797 100644
--- a/xmake/repository/templates/cuda/console/src/main.cu
+++ b/xmake/templates/cuda/console/src/main.cu
diff --git a/xmake/repository/templates/cuda/console/xmake.lua b/xmake/templates/cuda/console/xmake.lua
index fb393bab7..fb393bab7 100644
--- a/xmake/repository/templates/cuda/console/xmake.lua
+++ b/xmake/templates/cuda/console/xmake.lua
diff --git a/xmake/repository/templates/cuda/shared/inc/lib.cuh b/xmake/templates/cuda/shared/inc/lib.cuh
index 5565e3eb5..5565e3eb5 100644
--- a/xmake/repository/templates/cuda/shared/inc/lib.cuh
+++ b/xmake/templates/cuda/shared/inc/lib.cuh
diff --git a/xmake/repository/templates/cuda/shared/src/lib.cu b/xmake/templates/cuda/shared/src/lib.cu
index d2cff9ac2..d2cff9ac2 100644
--- a/xmake/repository/templates/cuda/shared/src/lib.cu
+++ b/xmake/templates/cuda/shared/src/lib.cu
diff --git a/xmake/repository/templates/cuda/shared/xmake.lua b/xmake/templates/cuda/shared/xmake.lua
index 5f53ff87b..5f53ff87b 100644
--- a/xmake/repository/templates/cuda/shared/xmake.lua
+++ b/xmake/templates/cuda/shared/xmake.lua
diff --git a/xmake/repository/templates/cuda/static/inc/lib.cuh b/xmake/templates/cuda/static/inc/lib.cuh
index 35255e31e..35255e31e 100644
--- a/xmake/repository/templates/cuda/static/inc/lib.cuh
+++ b/xmake/templates/cuda/static/inc/lib.cuh
diff --git a/xmake/repository/templates/cuda/static/src/lib.cu b/xmake/templates/cuda/static/src/lib.cu
index a5f054354..a5f054354 100644
--- a/xmake/repository/templates/cuda/static/src/lib.cu
+++ b/xmake/templates/cuda/static/src/lib.cu
diff --git a/xmake/repository/templates/cuda/static/xmake.lua b/xmake/templates/cuda/static/xmake.lua
index 7a582cfbb..7a582cfbb 100644
--- a/xmake/repository/templates/cuda/static/xmake.lua
+++ b/xmake/templates/cuda/static/xmake.lua
diff --git a/xmake/repository/templates/dlang/console/src/main.d b/xmake/templates/dlang/console/src/main.d
index 6bea6ec02..6bea6ec02 100644
--- a/xmake/repository/templates/dlang/console/src/main.d
+++ b/xmake/templates/dlang/console/src/main.d
diff --git a/xmake/repository/templates/dlang/console/xmake.lua b/xmake/templates/dlang/console/xmake.lua
index c242d60c2..c242d60c2 100644
--- a/xmake/repository/templates/dlang/console/xmake.lua
+++ b/xmake/templates/dlang/console/xmake.lua
diff --git a/xmake/repository/templates/dlang/shared/src/interfaces.d b/xmake/templates/dlang/shared/src/interfaces.d
index b4f7ab6ba..b4f7ab6ba 100644
--- a/xmake/repository/templates/dlang/shared/src/interfaces.d
+++ b/xmake/templates/dlang/shared/src/interfaces.d
diff --git a/xmake/repository/templates/dlang/shared/src/main.d b/xmake/templates/dlang/shared/src/main.d
index bfcf9cf56..bfcf9cf56 100644
--- a/xmake/repository/templates/dlang/shared/src/main.d
+++ b/xmake/templates/dlang/shared/src/main.d
diff --git a/xmake/repository/templates/dlang/shared/xmake.lua b/xmake/templates/dlang/shared/xmake.lua
index 0086bebae..0086bebae 100644
--- a/xmake/repository/templates/dlang/shared/xmake.lua
+++ b/xmake/templates/dlang/shared/xmake.lua
diff --git a/xmake/repository/templates/dlang/static/src/interfaces.d b/xmake/templates/dlang/static/src/interfaces.d
index 3386c9124..3386c9124 100644
--- a/xmake/repository/templates/dlang/static/src/interfaces.d
+++ b/xmake/templates/dlang/static/src/interfaces.d
diff --git a/xmake/repository/templates/dlang/static/src/main.d b/xmake/templates/dlang/static/src/main.d
index bfcf9cf56..bfcf9cf56 100644
--- a/xmake/repository/templates/dlang/static/src/main.d
+++ b/xmake/templates/dlang/static/src/main.d
diff --git a/xmake/repository/templates/dlang/static/xmake.lua b/xmake/templates/dlang/static/xmake.lua
index b8fb24b37..b8fb24b37 100644
--- a/xmake/repository/templates/dlang/static/xmake.lua
+++ b/xmake/templates/dlang/static/xmake.lua
diff --git a/xmake/repository/templates/fortran/console/src/main.f90 b/xmake/templates/fortran/console/src/main.f90
index a332e8401..a332e8401 100644
--- a/xmake/repository/templates/fortran/console/src/main.f90
+++ b/xmake/templates/fortran/console/src/main.f90
diff --git a/xmake/repository/templates/fortran/console/xmake.lua b/xmake/templates/fortran/console/xmake.lua
index 70faf8aea..70faf8aea 100644
--- a/xmake/repository/templates/fortran/console/xmake.lua
+++ b/xmake/templates/fortran/console/xmake.lua
diff --git a/xmake/repository/templates/fortran/shared/src/main.f90 b/xmake/templates/fortran/shared/src/main.f90
index 9adea3e26..9adea3e26 100644
--- a/xmake/repository/templates/fortran/shared/src/main.f90
+++ b/xmake/templates/fortran/shared/src/main.f90
diff --git a/xmake/repository/templates/fortran/shared/src/test.f90 b/xmake/templates/fortran/shared/src/test.f90
index 7b215fcdf..7b215fcdf 100644
--- a/xmake/repository/templates/fortran/shared/src/test.f90
+++ b/xmake/templates/fortran/shared/src/test.f90
diff --git a/xmake/repository/templates/fortran/shared/xmake.lua b/xmake/templates/fortran/shared/xmake.lua
index da6d6da3f..da6d6da3f 100644
--- a/xmake/repository/templates/fortran/shared/xmake.lua
+++ b/xmake/templates/fortran/shared/xmake.lua
diff --git a/xmake/repository/templates/fortran/static/src/main.f90 b/xmake/templates/fortran/static/src/main.f90
index 9adea3e26..9adea3e26 100644
--- a/xmake/repository/templates/fortran/static/src/main.f90
+++ b/xmake/templates/fortran/static/src/main.f90
diff --git a/xmake/repository/templates/fortran/static/src/test.f90 b/xmake/templates/fortran/static/src/test.f90
index 7b215fcdf..7b215fcdf 100644
--- a/xmake/repository/templates/fortran/static/src/test.f90
+++ b/xmake/templates/fortran/static/src/test.f90
diff --git a/xmake/repository/templates/fortran/static/xmake.lua b/xmake/templates/fortran/static/xmake.lua
index 4b91ad207..4b91ad207 100644
--- a/xmake/repository/templates/fortran/static/xmake.lua
+++ b/xmake/templates/fortran/static/xmake.lua
diff --git a/xmake/repository/templates/go/console/src/main.go b/xmake/templates/go/console/src/main.go
index b689db480..b689db480 100644
--- a/xmake/repository/templates/go/console/src/main.go
+++ b/xmake/templates/go/console/src/main.go
diff --git a/xmake/repository/templates/go/console/xmake.lua b/xmake/templates/go/console/xmake.lua
index a7861eb2a..a7861eb2a 100644
--- a/xmake/repository/templates/go/console/xmake.lua
+++ b/xmake/templates/go/console/xmake.lua
diff --git a/xmake/repository/templates/go/static/src/main.go b/xmake/templates/go/static/src/main.go
index f2b477453..f2b477453 100644
--- a/xmake/repository/templates/go/static/src/main.go
+++ b/xmake/templates/go/static/src/main.go
diff --git a/xmake/repository/templates/go/static/src/module/add.go b/xmake/templates/go/static/src/module/add.go
index 98ffef600..98ffef600 100644
--- a/xmake/repository/templates/go/static/src/module/add.go
+++ b/xmake/templates/go/static/src/module/add.go
diff --git a/xmake/repository/templates/go/static/src/module/sub.go b/xmake/templates/go/static/src/module/sub.go
index a24a9abb7..a24a9abb7 100644
--- a/xmake/repository/templates/go/static/src/module/sub.go
+++ b/xmake/templates/go/static/src/module/sub.go
diff --git a/xmake/repository/templates/go/static/src/test.go b/xmake/templates/go/static/src/test.go
index cf10523f9..cf10523f9 100644
--- a/xmake/repository/templates/go/static/src/test.go
+++ b/xmake/templates/go/static/src/test.go
diff --git a/xmake/repository/templates/go/static/xmake.lua b/xmake/templates/go/static/xmake.lua
index 4391d7799..4391d7799 100644
--- a/xmake/repository/templates/go/static/xmake.lua
+++ b/xmake/templates/go/static/xmake.lua
diff --git a/xmake/repository/templates/kotlin/console/src/main.kt b/xmake/templates/kotlin/console/src/main.kt
index 543f5ac8f..543f5ac8f 100644
--- a/xmake/repository/templates/kotlin/console/src/main.kt
+++ b/xmake/templates/kotlin/console/src/main.kt
diff --git a/xmake/repository/templates/kotlin/console/xmake.lua b/xmake/templates/kotlin/console/xmake.lua
index 935dc7c75..935dc7c75 100644
--- a/xmake/repository/templates/kotlin/console/xmake.lua
+++ b/xmake/templates/kotlin/console/xmake.lua
diff --git a/xmake/repository/templates/kotlin/shared/src/foo.kt b/xmake/templates/kotlin/shared/src/foo.kt
index cc5184bd7..cc5184bd7 100644
--- a/xmake/repository/templates/kotlin/shared/src/foo.kt
+++ b/xmake/templates/kotlin/shared/src/foo.kt
diff --git a/xmake/repository/templates/kotlin/shared/src/main.c b/xmake/templates/kotlin/shared/src/main.c
index abb3f2c12..abb3f2c12 100644
--- a/xmake/repository/templates/kotlin/shared/src/main.c
+++ b/xmake/templates/kotlin/shared/src/main.c
diff --git a/xmake/repository/templates/kotlin/shared/xmake.lua b/xmake/templates/kotlin/shared/xmake.lua
index 97bebf8a6..97bebf8a6 100644
--- a/xmake/repository/templates/kotlin/shared/xmake.lua
+++ b/xmake/templates/kotlin/shared/xmake.lua
diff --git a/xmake/repository/templates/kotlin/static/src/foo.kt b/xmake/templates/kotlin/static/src/foo.kt
index cc5184bd7..cc5184bd7 100644
--- a/xmake/repository/templates/kotlin/static/src/foo.kt
+++ b/xmake/templates/kotlin/static/src/foo.kt
diff --git a/xmake/repository/templates/kotlin/static/src/main.c b/xmake/templates/kotlin/static/src/main.c
index abb3f2c12..abb3f2c12 100644
--- a/xmake/repository/templates/kotlin/static/src/main.c
+++ b/xmake/templates/kotlin/static/src/main.c
diff --git a/xmake/repository/templates/kotlin/static/xmake.lua b/xmake/templates/kotlin/static/xmake.lua
index 6095dbe93..6095dbe93 100644
--- a/xmake/repository/templates/kotlin/static/xmake.lua
+++ b/xmake/templates/kotlin/static/xmake.lua
diff --git a/xmake/repository/templates/nim/console/src/main.nim b/xmake/templates/nim/console/src/main.nim
index 17179284f..17179284f 100644
--- a/xmake/repository/templates/nim/console/src/main.nim
+++ b/xmake/templates/nim/console/src/main.nim
diff --git a/xmake/repository/templates/nim/console/xmake.lua b/xmake/templates/nim/console/xmake.lua
index 662945bbb..662945bbb 100644
--- a/xmake/repository/templates/nim/console/xmake.lua
+++ b/xmake/templates/nim/console/xmake.lua
diff --git a/xmake/repository/templates/nim/shared/src/foo.nim b/xmake/templates/nim/shared/src/foo.nim
index cde4de7ca..cde4de7ca 100644
--- a/xmake/repository/templates/nim/shared/src/foo.nim
+++ b/xmake/templates/nim/shared/src/foo.nim
diff --git a/xmake/repository/templates/nim/shared/src/main.nim b/xmake/templates/nim/shared/src/main.nim
index 1be3abe69..1be3abe69 100644
--- a/xmake/repository/templates/nim/shared/src/main.nim
+++ b/xmake/templates/nim/shared/src/main.nim
diff --git a/xmake/repository/templates/nim/shared/xmake.lua b/xmake/templates/nim/shared/xmake.lua
index 5067dafb3..5067dafb3 100644
--- a/xmake/repository/templates/nim/shared/xmake.lua
+++ b/xmake/templates/nim/shared/xmake.lua
diff --git a/xmake/repository/templates/nim/static/src/foo.nim b/xmake/templates/nim/static/src/foo.nim
index be0904262..be0904262 100644
--- a/xmake/repository/templates/nim/static/src/foo.nim
+++ b/xmake/templates/nim/static/src/foo.nim
diff --git a/xmake/repository/templates/nim/static/src/main.nim b/xmake/templates/nim/static/src/main.nim
index 1be3abe69..1be3abe69 100644
--- a/xmake/repository/templates/nim/static/src/main.nim
+++ b/xmake/templates/nim/static/src/main.nim
diff --git a/xmake/repository/templates/nim/static/xmake.lua b/xmake/templates/nim/static/xmake.lua
index 3f2cb7dfc..3f2cb7dfc 100644
--- a/xmake/repository/templates/nim/static/xmake.lua
+++ b/xmake/templates/nim/static/xmake.lua
diff --git a/xmake/repository/templates/objc++/console/src/main.mm b/xmake/templates/objc++/console/src/main.mm
index cbd1e8906..cbd1e8906 100644
--- a/xmake/repository/templates/objc++/console/src/main.mm
+++ b/xmake/templates/objc++/console/src/main.mm
diff --git a/xmake/repository/templates/objc++/console/xmake.lua b/xmake/templates/objc++/console/xmake.lua
index 99749f901..99749f901 100644
--- a/xmake/repository/templates/objc++/console/xmake.lua
+++ b/xmake/templates/objc++/console/xmake.lua
diff --git a/xmake/repository/templates/objc++/xcode/bundle/src/Info.plist b/xmake/templates/objc++/xcode/bundle/src/Info.plist
index 20608314e..20608314e 100644
--- a/xmake/repository/templates/objc++/xcode/bundle/src/Info.plist
+++ b/xmake/templates/objc++/xcode/bundle/src/Info.plist
diff --git a/xmake/repository/templates/objc++/xcode/bundle/src/test.h b/xmake/templates/objc++/xcode/bundle/src/test.h
index d882546d1..d882546d1 100644
--- a/xmake/repository/templates/objc++/xcode/bundle/src/test.h
+++ b/xmake/templates/objc++/xcode/bundle/src/test.h
diff --git a/xmake/repository/templates/objc++/xcode/bundle/src/test.mm b/xmake/templates/objc++/xcode/bundle/src/test.mm
index 20e29bd0a..20e29bd0a 100644
--- a/xmake/repository/templates/objc++/xcode/bundle/src/test.mm
+++ b/xmake/templates/objc++/xcode/bundle/src/test.mm
diff --git a/xmake/repository/templates/objc++/xcode/bundle/xmake.lua b/xmake/templates/objc++/xcode/bundle/xmake.lua
index 1ecf69fcb..1ecf69fcb 100644
--- a/xmake/repository/templates/objc++/xcode/bundle/xmake.lua
+++ b/xmake/templates/objc++/xcode/bundle/xmake.lua
diff --git a/xmake/repository/templates/objc++/xcode/framework/src/Info.plist b/xmake/templates/objc++/xcode/framework/src/Info.plist
index 20608314e..20608314e 100644
--- a/xmake/repository/templates/objc++/xcode/framework/src/Info.plist
+++ b/xmake/templates/objc++/xcode/framework/src/Info.plist
diff --git a/xmake/repository/templates/objc++/xcode/framework/src/test.h b/xmake/templates/objc++/xcode/framework/src/test.h
index d882546d1..d882546d1 100644
--- a/xmake/repository/templates/objc++/xcode/framework/src/test.h
+++ b/xmake/templates/objc++/xcode/framework/src/test.h
diff --git a/xmake/repository/templates/objc++/xcode/framework/src/test.mm b/xmake/templates/objc++/xcode/framework/src/test.mm
index 20e29bd0a..20e29bd0a 100644
--- a/xmake/repository/templates/objc++/xcode/framework/src/test.mm
+++ b/xmake/templates/objc++/xcode/framework/src/test.mm
diff --git a/xmake/repository/templates/objc++/xcode/framework/xmake.lua b/xmake/templates/objc++/xcode/framework/xmake.lua
index 3805d95c6..3805d95c6 100644
--- a/xmake/repository/templates/objc++/xcode/framework/xmake.lua
+++ b/xmake/templates/objc++/xcode/framework/xmake.lua
diff --git a/xmake/repository/templates/objc/console/src/main.m b/xmake/templates/objc/console/src/main.m
index cbd1e8906..cbd1e8906 100644
--- a/xmake/repository/templates/objc/console/src/main.m
+++ b/xmake/templates/objc/console/src/main.m
diff --git a/xmake/repository/templates/objc/console/xmake.lua b/xmake/templates/objc/console/xmake.lua
index 37c01442a..37c01442a 100644
--- a/xmake/repository/templates/objc/console/xmake.lua
+++ b/xmake/templates/objc/console/xmake.lua
diff --git a/xmake/repository/templates/objc/xcode/bundle/src/Info.plist b/xmake/templates/objc/xcode/bundle/src/Info.plist
index 20608314e..20608314e 100644
--- a/xmake/repository/templates/objc/xcode/bundle/src/Info.plist
+++ b/xmake/templates/objc/xcode/bundle/src/Info.plist
diff --git a/xmake/repository/templates/objc/xcode/bundle/src/test.h b/xmake/templates/objc/xcode/bundle/src/test.h
index d882546d1..d882546d1 100644
--- a/xmake/repository/templates/objc/xcode/bundle/src/test.h
+++ b/xmake/templates/objc/xcode/bundle/src/test.h
diff --git a/xmake/repository/templates/objc/xcode/bundle/src/test.m b/xmake/templates/objc/xcode/bundle/src/test.m
index 20e29bd0a..20e29bd0a 100644
--- a/xmake/repository/templates/objc/xcode/bundle/src/test.m
+++ b/xmake/templates/objc/xcode/bundle/src/test.m
diff --git a/xmake/repository/templates/objc/xcode/bundle/xmake.lua b/xmake/templates/objc/xcode/bundle/xmake.lua
index 34d108fcb..34d108fcb 100644
--- a/xmake/repository/templates/objc/xcode/bundle/xmake.lua
+++ b/xmake/templates/objc/xcode/bundle/xmake.lua
diff --git a/xmake/repository/templates/objc/xcode/framework/src/Info.plist b/xmake/templates/objc/xcode/framework/src/Info.plist
index 20608314e..20608314e 100644
--- a/xmake/repository/templates/objc/xcode/framework/src/Info.plist
+++ b/xmake/templates/objc/xcode/framework/src/Info.plist
diff --git a/xmake/repository/templates/objc/xcode/framework/src/test.h b/xmake/templates/objc/xcode/framework/src/test.h
index d882546d1..d882546d1 100644
--- a/xmake/repository/templates/objc/xcode/framework/src/test.h
+++ b/xmake/templates/objc/xcode/framework/src/test.h
diff --git a/xmake/repository/templates/objc/xcode/framework/src/test.m b/xmake/templates/objc/xcode/framework/src/test.m
index 20e29bd0a..20e29bd0a 100644
--- a/xmake/repository/templates/objc/xcode/framework/src/test.m
+++ b/xmake/templates/objc/xcode/framework/src/test.m
diff --git a/xmake/repository/templates/objc/xcode/framework/xmake.lua b/xmake/templates/objc/xcode/framework/xmake.lua
index 83ddfa1a2..83ddfa1a2 100644
--- a/xmake/repository/templates/objc/xcode/framework/xmake.lua
+++ b/xmake/templates/objc/xcode/framework/xmake.lua
diff --git a/xmake/repository/templates/objc/xcode/iosapp/src/AppDelegate.h b/xmake/templates/objc/xcode/iosapp/src/AppDelegate.h
index b01a8dc72..b01a8dc72 100644
--- a/xmake/repository/templates/objc/xcode/iosapp/src/AppDelegate.h
+++ b/xmake/templates/objc/xcode/iosapp/src/AppDelegate.h
diff --git a/xmake/repository/templates/objc/xcode/iosapp/src/AppDelegate.m b/xmake/templates/objc/xcode/iosapp/src/AppDelegate.m
index 0ad500bfe..0ad500bfe 100644
--- a/xmake/repository/templates/objc/xcode/iosapp/src/AppDelegate.m
+++ b/xmake/templates/objc/xcode/iosapp/src/AppDelegate.m
diff --git a/xmake/repository/templates/objc/xcode/iosapp/src/Assets.xcassets/AppIcon.appiconset/Contents.json b/xmake/templates/objc/xcode/iosapp/src/Assets.xcassets/AppIcon.appiconset/Contents.json
index d8db8d65f..d8db8d65f 100644
--- a/xmake/repository/templates/objc/xcode/iosapp/src/Assets.xcassets/AppIcon.appiconset/Contents.json
+++ b/xmake/templates/objc/xcode/iosapp/src/Assets.xcassets/AppIcon.appiconset/Contents.json
diff --git a/xmake/repository/templates/objc/xcode/iosapp/src/Assets.xcassets/Contents.json b/xmake/templates/objc/xcode/iosapp/src/Assets.xcassets/Contents.json
index da4a164c9..da4a164c9 100644
--- a/xmake/repository/templates/objc/xcode/iosapp/src/Assets.xcassets/Contents.json
+++ b/xmake/templates/objc/xcode/iosapp/src/Assets.xcassets/Contents.json
diff --git a/xmake/repository/templates/objc/xcode/iosapp/src/Base.lproj/LaunchScreen.storyboard b/xmake/templates/objc/xcode/iosapp/src/Base.lproj/LaunchScreen.storyboard
index 865e9329f..865e9329f 100644
--- a/xmake/repository/templates/objc/xcode/iosapp/src/Base.lproj/LaunchScreen.storyboard
+++ b/xmake/templates/objc/xcode/iosapp/src/Base.lproj/LaunchScreen.storyboard
diff --git a/xmake/repository/templates/objc/xcode/iosapp/src/Base.lproj/Main.storyboard b/xmake/templates/objc/xcode/iosapp/src/Base.lproj/Main.storyboard
index 808a21ce7..808a21ce7 100644
--- a/xmake/repository/templates/objc/xcode/iosapp/src/Base.lproj/Main.storyboard
+++ b/xmake/templates/objc/xcode/iosapp/src/Base.lproj/Main.storyboard
diff --git a/xmake/repository/templates/objc/xcode/iosapp/src/Info.plist b/xmake/templates/objc/xcode/iosapp/src/Info.plist
index 686d21a3a..686d21a3a 100644
--- a/xmake/repository/templates/objc/xcode/iosapp/src/Info.plist
+++ b/xmake/templates/objc/xcode/iosapp/src/Info.plist
diff --git a/xmake/repository/templates/objc/xcode/iosapp/src/SceneDelegate.h b/xmake/templates/objc/xcode/iosapp/src/SceneDelegate.h
index 5dbf88aa6..5dbf88aa6 100644
--- a/xmake/repository/templates/objc/xcode/iosapp/src/SceneDelegate.h
+++ b/xmake/templates/objc/xcode/iosapp/src/SceneDelegate.h
diff --git a/xmake/repository/templates/objc/xcode/iosapp/src/SceneDelegate.m b/xmake/templates/objc/xcode/iosapp/src/SceneDelegate.m
index d6698dd7c..d6698dd7c 100644
--- a/xmake/repository/templates/objc/xcode/iosapp/src/SceneDelegate.m
+++ b/xmake/templates/objc/xcode/iosapp/src/SceneDelegate.m
diff --git a/xmake/repository/templates/objc/xcode/iosapp/src/ViewController.h b/xmake/templates/objc/xcode/iosapp/src/ViewController.h
index e74d2f236..e74d2f236 100644
--- a/xmake/repository/templates/objc/xcode/iosapp/src/ViewController.h
+++ b/xmake/templates/objc/xcode/iosapp/src/ViewController.h
diff --git a/xmake/repository/templates/objc/xcode/iosapp/src/ViewController.m b/xmake/templates/objc/xcode/iosapp/src/ViewController.m
index c3441c87a..c3441c87a 100644
--- a/xmake/repository/templates/objc/xcode/iosapp/src/ViewController.m
+++ b/xmake/templates/objc/xcode/iosapp/src/ViewController.m
diff --git a/xmake/repository/templates/objc/xcode/iosapp/src/main.m b/xmake/templates/objc/xcode/iosapp/src/main.m
index 11a58afc7..11a58afc7 100644
--- a/xmake/repository/templates/objc/xcode/iosapp/src/main.m
+++ b/xmake/templates/objc/xcode/iosapp/src/main.m
diff --git a/xmake/repository/templates/objc/xcode/iosapp/xmake.lua b/xmake/templates/objc/xcode/iosapp/xmake.lua
index 1306fce94..1306fce94 100644
--- a/xmake/repository/templates/objc/xcode/iosapp/xmake.lua
+++ b/xmake/templates/objc/xcode/iosapp/xmake.lua
diff --git a/xmake/repository/templates/objc/xcode/iosapp_with_framework/src/app/AppDelegate.h b/xmake/templates/objc/xcode/iosapp_with_framework/src/app/AppDelegate.h
index b01a8dc72..b01a8dc72 100644
--- a/xmake/repository/templates/objc/xcode/iosapp_with_framework/src/app/AppDelegate.h
+++ b/xmake/templates/objc/xcode/iosapp_with_framework/src/app/AppDelegate.h
diff --git a/xmake/repository/templates/objc/xcode/iosapp_with_framework/src/app/AppDelegate.m b/xmake/templates/objc/xcode/iosapp_with_framework/src/app/AppDelegate.m
index 1f3556c06..1f3556c06 100644
--- a/xmake/repository/templates/objc/xcode/iosapp_with_framework/src/app/AppDelegate.m
+++ b/xmake/templates/objc/xcode/iosapp_with_framework/src/app/AppDelegate.m
diff --git a/xmake/repository/templates/objc/xcode/iosapp_with_framework/src/app/Assets.xcassets/AppIcon.appiconset/Contents.json b/xmake/templates/objc/xcode/iosapp_with_framework/src/app/Assets.xcassets/AppIcon.appiconset/Contents.json
index d8db8d65f..d8db8d65f 100644
--- a/xmake/repository/templates/objc/xcode/iosapp_with_framework/src/app/Assets.xcassets/AppIcon.appiconset/Contents.json
+++ b/xmake/templates/objc/xcode/iosapp_with_framework/src/app/Assets.xcassets/AppIcon.appiconset/Contents.json
diff --git a/xmake/repository/templates/objc/xcode/iosapp_with_framework/src/app/Assets.xcassets/Contents.json b/xmake/templates/objc/xcode/iosapp_with_framework/src/app/Assets.xcassets/Contents.json
index da4a164c9..da4a164c9 100644
--- a/xmake/repository/templates/objc/xcode/iosapp_with_framework/src/app/Assets.xcassets/Contents.json
+++ b/xmake/templates/objc/xcode/iosapp_with_framework/src/app/Assets.xcassets/Contents.json
diff --git a/xmake/repository/templates/objc/xcode/iosapp_with_framework/src/app/Base.lproj/LaunchScreen.storyboard b/xmake/templates/objc/xcode/iosapp_with_framework/src/app/Base.lproj/LaunchScreen.storyboard
index 865e9329f..865e9329f 100644
--- a/xmake/repository/templates/objc/xcode/iosapp_with_framework/src/app/Base.lproj/LaunchScreen.storyboard
+++ b/xmake/templates/objc/xcode/iosapp_with_framework/src/app/Base.lproj/LaunchScreen.storyboard
diff --git a/xmake/repository/templates/objc/xcode/iosapp_with_framework/src/app/Base.lproj/Main.storyboard b/xmake/templates/objc/xcode/iosapp_with_framework/src/app/Base.lproj/Main.storyboard
index 808a21ce7..808a21ce7 100644
--- a/xmake/repository/templates/objc/xcode/iosapp_with_framework/src/app/Base.lproj/Main.storyboard
+++ b/xmake/templates/objc/xcode/iosapp_with_framework/src/app/Base.lproj/Main.storyboard
diff --git a/xmake/repository/templates/objc/xcode/iosapp_with_framework/src/app/Info.plist b/xmake/templates/objc/xcode/iosapp_with_framework/src/app/Info.plist
index 686d21a3a..686d21a3a 100644
--- a/xmake/repository/templates/objc/xcode/iosapp_with_framework/src/app/Info.plist
+++ b/xmake/templates/objc/xcode/iosapp_with_framework/src/app/Info.plist
diff --git a/xmake/repository/templates/objc/xcode/iosapp_with_framework/src/app/SceneDelegate.h b/xmake/templates/objc/xcode/iosapp_with_framework/src/app/SceneDelegate.h
index 5dbf88aa6..5dbf88aa6 100644
--- a/xmake/repository/templates/objc/xcode/iosapp_with_framework/src/app/SceneDelegate.h
+++ b/xmake/templates/objc/xcode/iosapp_with_framework/src/app/SceneDelegate.h
diff --git a/xmake/repository/templates/objc/xcode/iosapp_with_framework/src/app/SceneDelegate.m b/xmake/templates/objc/xcode/iosapp_with_framework/src/app/SceneDelegate.m
index d6698dd7c..d6698dd7c 100644
--- a/xmake/repository/templates/objc/xcode/iosapp_with_framework/src/app/SceneDelegate.m
+++ b/xmake/templates/objc/xcode/iosapp_with_framework/src/app/SceneDelegate.m
diff --git a/xmake/repository/templates/objc/xcode/iosapp_with_framework/src/app/ViewController.h b/xmake/templates/objc/xcode/iosapp_with_framework/src/app/ViewController.h
index e74d2f236..e74d2f236 100644
--- a/xmake/repository/templates/objc/xcode/iosapp_with_framework/src/app/ViewController.h
+++ b/xmake/templates/objc/xcode/iosapp_with_framework/src/app/ViewController.h
diff --git a/xmake/repository/templates/objc/xcode/iosapp_with_framework/src/app/ViewController.m b/xmake/templates/objc/xcode/iosapp_with_framework/src/app/ViewController.m
index c3441c87a..c3441c87a 100644
--- a/xmake/repository/templates/objc/xcode/iosapp_with_framework/src/app/ViewController.m
+++ b/xmake/templates/objc/xcode/iosapp_with_framework/src/app/ViewController.m
diff --git a/xmake/repository/templates/objc/xcode/iosapp_with_framework/src/app/main.m b/xmake/templates/objc/xcode/iosapp_with_framework/src/app/main.m
index 11a58afc7..11a58afc7 100644
--- a/xmake/repository/templates/objc/xcode/iosapp_with_framework/src/app/main.m
+++ b/xmake/templates/objc/xcode/iosapp_with_framework/src/app/main.m
diff --git a/xmake/repository/templates/objc/xcode/iosapp_with_framework/src/framework/Info.plist b/xmake/templates/objc/xcode/iosapp_with_framework/src/framework/Info.plist
index 20608314e..20608314e 100644
--- a/xmake/repository/templates/objc/xcode/iosapp_with_framework/src/framework/Info.plist
+++ b/xmake/templates/objc/xcode/iosapp_with_framework/src/framework/Info.plist
diff --git a/xmake/repository/templates/objc/xcode/iosapp_with_framework/src/framework/test.h b/xmake/templates/objc/xcode/iosapp_with_framework/src/framework/test.h
index d882546d1..d882546d1 100644
--- a/xmake/repository/templates/objc/xcode/iosapp_with_framework/src/framework/test.h
+++ b/xmake/templates/objc/xcode/iosapp_with_framework/src/framework/test.h
diff --git a/xmake/repository/templates/objc/xcode/iosapp_with_framework/src/framework/test.m b/xmake/templates/objc/xcode/iosapp_with_framework/src/framework/test.m
index 71214544b..71214544b 100644
--- a/xmake/repository/templates/objc/xcode/iosapp_with_framework/src/framework/test.m
+++ b/xmake/templates/objc/xcode/iosapp_with_framework/src/framework/test.m
diff --git a/xmake/repository/templates/objc/xcode/iosapp_with_framework/xmake.lua b/xmake/templates/objc/xcode/iosapp_with_framework/xmake.lua
index e1383b8b2..e1383b8b2 100644
--- a/xmake/repository/templates/objc/xcode/iosapp_with_framework/xmake.lua
+++ b/xmake/templates/objc/xcode/iosapp_with_framework/xmake.lua
diff --git a/xmake/repository/templates/objc/xcode/macapp/src/AppDelegate.h b/xmake/templates/objc/xcode/macapp/src/AppDelegate.h
index 5f5ba059e..5f5ba059e 100644
--- a/xmake/repository/templates/objc/xcode/macapp/src/AppDelegate.h
+++ b/xmake/templates/objc/xcode/macapp/src/AppDelegate.h
diff --git a/xmake/repository/templates/objc/xcode/macapp/src/AppDelegate.m b/xmake/templates/objc/xcode/macapp/src/AppDelegate.m
index da967d2ef..da967d2ef 100644
--- a/xmake/repository/templates/objc/xcode/macapp/src/AppDelegate.m
+++ b/xmake/templates/objc/xcode/macapp/src/AppDelegate.m
diff --git a/xmake/repository/templates/objc/xcode/macapp/src/Assets.xcassets/AppIcon.appiconset/Contents.json b/xmake/templates/objc/xcode/macapp/src/Assets.xcassets/AppIcon.appiconset/Contents.json
index 2db2b1c7c..2db2b1c7c 100644
--- a/xmake/repository/templates/objc/xcode/macapp/src/Assets.xcassets/AppIcon.appiconset/Contents.json
+++ b/xmake/templates/objc/xcode/macapp/src/Assets.xcassets/AppIcon.appiconset/Contents.json
diff --git a/xmake/repository/templates/objc/xcode/macapp/src/Assets.xcassets/Contents.json b/xmake/templates/objc/xcode/macapp/src/Assets.xcassets/Contents.json
index da4a164c9..da4a164c9 100644
--- a/xmake/repository/templates/objc/xcode/macapp/src/Assets.xcassets/Contents.json
+++ b/xmake/templates/objc/xcode/macapp/src/Assets.xcassets/Contents.json
diff --git a/xmake/repository/templates/objc/xcode/macapp/src/Base.lproj/Main.storyboard b/xmake/templates/objc/xcode/macapp/src/Base.lproj/Main.storyboard
index 7a6d659a5..7a6d659a5 100644
--- a/xmake/repository/templates/objc/xcode/macapp/src/Base.lproj/Main.storyboard
+++ b/xmake/templates/objc/xcode/macapp/src/Base.lproj/Main.storyboard
diff --git a/xmake/repository/templates/objc/xcode/macapp/src/Info.plist b/xmake/templates/objc/xcode/macapp/src/Info.plist
index ce53d780c..ce53d780c 100644
--- a/xmake/repository/templates/objc/xcode/macapp/src/Info.plist
+++ b/xmake/templates/objc/xcode/macapp/src/Info.plist
diff --git a/xmake/repository/templates/objc/xcode/macapp/src/ViewController.h b/xmake/templates/objc/xcode/macapp/src/ViewController.h
index a974fc934..a974fc934 100644
--- a/xmake/repository/templates/objc/xcode/macapp/src/ViewController.h
+++ b/xmake/templates/objc/xcode/macapp/src/ViewController.h
diff --git a/xmake/repository/templates/objc/xcode/macapp/src/ViewController.m b/xmake/templates/objc/xcode/macapp/src/ViewController.m
index bccdf6189..bccdf6189 100644
--- a/xmake/repository/templates/objc/xcode/macapp/src/ViewController.m
+++ b/xmake/templates/objc/xcode/macapp/src/ViewController.m
diff --git a/xmake/repository/templates/objc/xcode/macapp/src/main.m b/xmake/templates/objc/xcode/macapp/src/main.m
index ae3706f51..ae3706f51 100644
--- a/xmake/repository/templates/objc/xcode/macapp/src/main.m
+++ b/xmake/templates/objc/xcode/macapp/src/main.m
diff --git a/xmake/repository/templates/objc/xcode/macapp/src/test.entitlements b/xmake/templates/objc/xcode/macapp/src/test.entitlements
index f2ef3ae02..f2ef3ae02 100644
--- a/xmake/repository/templates/objc/xcode/macapp/src/test.entitlements
+++ b/xmake/templates/objc/xcode/macapp/src/test.entitlements
diff --git a/xmake/repository/templates/objc/xcode/macapp/xmake.lua b/xmake/templates/objc/xcode/macapp/xmake.lua
index 1306fce94..1306fce94 100644
--- a/xmake/repository/templates/objc/xcode/macapp/xmake.lua
+++ b/xmake/templates/objc/xcode/macapp/xmake.lua
diff --git a/xmake/repository/templates/objc/xcode/macapp_with_framework/src/app/AppDelegate.h b/xmake/templates/objc/xcode/macapp_with_framework/src/app/AppDelegate.h
index 5f5ba059e..5f5ba059e 100644
--- a/xmake/repository/templates/objc/xcode/macapp_with_framework/src/app/AppDelegate.h
+++ b/xmake/templates/objc/xcode/macapp_with_framework/src/app/AppDelegate.h
diff --git a/xmake/repository/templates/objc/xcode/macapp_with_framework/src/app/AppDelegate.m b/xmake/templates/objc/xcode/macapp_with_framework/src/app/AppDelegate.m
index 667420c8c..667420c8c 100644
--- a/xmake/repository/templates/objc/xcode/macapp_with_framework/src/app/AppDelegate.m
+++ b/xmake/templates/objc/xcode/macapp_with_framework/src/app/AppDelegate.m
diff --git a/xmake/repository/templates/objc/xcode/macapp_with_framework/src/app/Assets.xcassets/AppIcon.appiconset/Contents.json b/xmake/templates/objc/xcode/macapp_with_framework/src/app/Assets.xcassets/AppIcon.appiconset/Contents.json
index 2db2b1c7c..2db2b1c7c 100644
--- a/xmake/repository/templates/objc/xcode/macapp_with_framework/src/app/Assets.xcassets/AppIcon.appiconset/Contents.json
+++ b/xmake/templates/objc/xcode/macapp_with_framework/src/app/Assets.xcassets/AppIcon.appiconset/Contents.json
diff --git a/xmake/repository/templates/objc/xcode/macapp_with_framework/src/app/Assets.xcassets/Contents.json b/xmake/templates/objc/xcode/macapp_with_framework/src/app/Assets.xcassets/Contents.json
index da4a164c9..da4a164c9 100644
--- a/xmake/repository/templates/objc/xcode/macapp_with_framework/src/app/Assets.xcassets/Contents.json
+++ b/xmake/templates/objc/xcode/macapp_with_framework/src/app/Assets.xcassets/Contents.json
diff --git a/xmake/repository/templates/objc/xcode/macapp_with_framework/src/app/Base.lproj/Main.storyboard b/xmake/templates/objc/xcode/macapp_with_framework/src/app/Base.lproj/Main.storyboard
index 7a6d659a5..7a6d659a5 100644
--- a/xmake/repository/templates/objc/xcode/macapp_with_framework/src/app/Base.lproj/Main.storyboard
+++ b/xmake/templates/objc/xcode/macapp_with_framework/src/app/Base.lproj/Main.storyboard
diff --git a/xmake/repository/templates/objc/xcode/macapp_with_framework/src/app/Info.plist b/xmake/templates/objc/xcode/macapp_with_framework/src/app/Info.plist
index ce53d780c..ce53d780c 100644
--- a/xmake/repository/templates/objc/xcode/macapp_with_framework/src/app/Info.plist
+++ b/xmake/templates/objc/xcode/macapp_with_framework/src/app/Info.plist
diff --git a/xmake/repository/templates/objc/xcode/macapp_with_framework/src/app/ViewController.h b/xmake/templates/objc/xcode/macapp_with_framework/src/app/ViewController.h
index a974fc934..a974fc934 100644
--- a/xmake/repository/templates/objc/xcode/macapp_with_framework/src/app/ViewController.h
+++ b/xmake/templates/objc/xcode/macapp_with_framework/src/app/ViewController.h
diff --git a/xmake/repository/templates/objc/xcode/macapp_with_framework/src/app/ViewController.m b/xmake/templates/objc/xcode/macapp_with_framework/src/app/ViewController.m
index bccdf6189..bccdf6189 100644
--- a/xmake/repository/templates/objc/xcode/macapp_with_framework/src/app/ViewController.m
+++ b/xmake/templates/objc/xcode/macapp_with_framework/src/app/ViewController.m
diff --git a/xmake/repository/templates/objc/xcode/macapp_with_framework/src/app/main.m b/xmake/templates/objc/xcode/macapp_with_framework/src/app/main.m
index ae3706f51..ae3706f51 100644
--- a/xmake/repository/templates/objc/xcode/macapp_with_framework/src/app/main.m
+++ b/xmake/templates/objc/xcode/macapp_with_framework/src/app/main.m
diff --git a/xmake/repository/templates/objc/xcode/macapp_with_framework/src/app/test.entitlements b/xmake/templates/objc/xcode/macapp_with_framework/src/app/test.entitlements
index f2ef3ae02..f2ef3ae02 100644
--- a/xmake/repository/templates/objc/xcode/macapp_with_framework/src/app/test.entitlements
+++ b/xmake/templates/objc/xcode/macapp_with_framework/src/app/test.entitlements
diff --git a/xmake/repository/templates/objc/xcode/macapp_with_framework/src/framework/Info.plist b/xmake/templates/objc/xcode/macapp_with_framework/src/framework/Info.plist
index 20608314e..20608314e 100644
--- a/xmake/repository/templates/objc/xcode/macapp_with_framework/src/framework/Info.plist
+++ b/xmake/templates/objc/xcode/macapp_with_framework/src/framework/Info.plist
diff --git a/xmake/repository/templates/objc/xcode/macapp_with_framework/src/framework/test.h b/xmake/templates/objc/xcode/macapp_with_framework/src/framework/test.h
index d882546d1..d882546d1 100644
--- a/xmake/repository/templates/objc/xcode/macapp_with_framework/src/framework/test.h
+++ b/xmake/templates/objc/xcode/macapp_with_framework/src/framework/test.h
diff --git a/xmake/repository/templates/objc/xcode/macapp_with_framework/src/framework/test.m b/xmake/templates/objc/xcode/macapp_with_framework/src/framework/test.m
index 71214544b..71214544b 100644
--- a/xmake/repository/templates/objc/xcode/macapp_with_framework/src/framework/test.m
+++ b/xmake/templates/objc/xcode/macapp_with_framework/src/framework/test.m
diff --git a/xmake/repository/templates/objc/xcode/macapp_with_framework/xmake.lua b/xmake/templates/objc/xcode/macapp_with_framework/xmake.lua
index e1383b8b2..e1383b8b2 100644
--- a/xmake/repository/templates/objc/xcode/macapp_with_framework/xmake.lua
+++ b/xmake/templates/objc/xcode/macapp_with_framework/xmake.lua
diff --git a/xmake/repository/templates/pascal/console/src/main.pas b/xmake/templates/pascal/console/src/main.pas
index a908ffab7..a908ffab7 100644
--- a/xmake/repository/templates/pascal/console/src/main.pas
+++ b/xmake/templates/pascal/console/src/main.pas
diff --git a/xmake/repository/templates/pascal/console/xmake.lua b/xmake/templates/pascal/console/xmake.lua
index 52168c3a4..52168c3a4 100644
--- a/xmake/repository/templates/pascal/console/xmake.lua
+++ b/xmake/templates/pascal/console/xmake.lua
diff --git a/xmake/repository/templates/pascal/shared/src/foo.pas b/xmake/templates/pascal/shared/src/foo.pas
index 788950f46..788950f46 100644
--- a/xmake/repository/templates/pascal/shared/src/foo.pas
+++ b/xmake/templates/pascal/shared/src/foo.pas
diff --git a/xmake/repository/templates/pascal/shared/src/main.pas b/xmake/templates/pascal/shared/src/main.pas
index 02d5e88d0..02d5e88d0 100644
--- a/xmake/repository/templates/pascal/shared/src/main.pas
+++ b/xmake/templates/pascal/shared/src/main.pas
diff --git a/xmake/repository/templates/pascal/shared/xmake.lua b/xmake/templates/pascal/shared/xmake.lua
index fad68bd07..fad68bd07 100644
--- a/xmake/repository/templates/pascal/shared/xmake.lua
+++ b/xmake/templates/pascal/shared/xmake.lua
diff --git a/xmake/repository/templates/rust/console/src/main.rs b/xmake/templates/rust/console/src/main.rs
index 88d5e2749..88d5e2749 100644
--- a/xmake/repository/templates/rust/console/src/main.rs
+++ b/xmake/templates/rust/console/src/main.rs
diff --git a/xmake/repository/templates/rust/console/xmake.lua b/xmake/templates/rust/console/xmake.lua
index 684ea720a..684ea720a 100644
--- a/xmake/repository/templates/rust/console/xmake.lua
+++ b/xmake/templates/rust/console/xmake.lua
diff --git a/xmake/repository/templates/rust/shared/src/foo.rs b/xmake/templates/rust/shared/src/foo.rs
index a3a3aa4a6..a3a3aa4a6 100644
--- a/xmake/repository/templates/rust/shared/src/foo.rs
+++ b/xmake/templates/rust/shared/src/foo.rs
diff --git a/xmake/repository/templates/rust/shared/src/main.rs b/xmake/templates/rust/shared/src/main.rs
index 205d23da3..205d23da3 100644
--- a/xmake/repository/templates/rust/shared/src/main.rs
+++ b/xmake/templates/rust/shared/src/main.rs
diff --git a/xmake/repository/templates/rust/shared/xmake.lua b/xmake/templates/rust/shared/xmake.lua
index 52effc17b..52effc17b 100644
--- a/xmake/repository/templates/rust/shared/xmake.lua
+++ b/xmake/templates/rust/shared/xmake.lua
diff --git a/xmake/repository/templates/rust/static/src/foo.rs b/xmake/templates/rust/static/src/foo.rs
index a3a3aa4a6..a3a3aa4a6 100644
--- a/xmake/repository/templates/rust/static/src/foo.rs
+++ b/xmake/templates/rust/static/src/foo.rs
diff --git a/xmake/repository/templates/rust/static/src/main.rs b/xmake/templates/rust/static/src/main.rs
index 205d23da3..205d23da3 100644
--- a/xmake/repository/templates/rust/static/src/main.rs
+++ b/xmake/templates/rust/static/src/main.rs
diff --git a/xmake/repository/templates/rust/static/xmake.lua b/xmake/templates/rust/static/xmake.lua
index 9eb48bef1..9eb48bef1 100644
--- a/xmake/repository/templates/rust/static/xmake.lua
+++ b/xmake/templates/rust/static/xmake.lua
diff --git a/xmake/repository/templates/swift/console/src/main.swift b/xmake/templates/swift/console/src/main.swift
index ee21045f4..ee21045f4 100644
--- a/xmake/repository/templates/swift/console/src/main.swift
+++ b/xmake/templates/swift/console/src/main.swift
diff --git a/xmake/repository/templates/swift/console/xmake.lua b/xmake/templates/swift/console/xmake.lua
index d07f6547b..d07f6547b 100644
--- a/xmake/repository/templates/swift/console/xmake.lua
+++ b/xmake/templates/swift/console/xmake.lua
diff --git a/xmake/repository/templates/vala/console/src/main.vala b/xmake/templates/vala/console/src/main.vala
index 8e8a32da0..8e8a32da0 100644
--- a/xmake/repository/templates/vala/console/src/main.vala
+++ b/xmake/templates/vala/console/src/main.vala
diff --git a/xmake/repository/templates/vala/console/xmake.lua b/xmake/templates/vala/console/xmake.lua
index 4fb124a8a..4fb124a8a 100644
--- a/xmake/repository/templates/vala/console/xmake.lua
+++ b/xmake/templates/vala/console/xmake.lua
diff --git a/xmake/repository/templates/vala/shared/src/main.vala b/xmake/templates/vala/shared/src/main.vala
index da9fd895e..da9fd895e 100644
--- a/xmake/repository/templates/vala/shared/src/main.vala
+++ b/xmake/templates/vala/shared/src/main.vala
diff --git a/xmake/repository/templates/vala/shared/src/mymath.vala b/xmake/templates/vala/shared/src/mymath.vala
index 4f4e761c5..4f4e761c5 100644
--- a/xmake/repository/templates/vala/shared/src/mymath.vala
+++ b/xmake/templates/vala/shared/src/mymath.vala
diff --git a/xmake/repository/templates/vala/shared/xmake.lua b/xmake/templates/vala/shared/xmake.lua
index 4b4291a78..4b4291a78 100644
--- a/xmake/repository/templates/vala/shared/xmake.lua
+++ b/xmake/templates/vala/shared/xmake.lua
diff --git a/xmake/repository/templates/vala/static/src/main.vala b/xmake/templates/vala/static/src/main.vala
index da9fd895e..da9fd895e 100644
--- a/xmake/repository/templates/vala/static/src/main.vala
+++ b/xmake/templates/vala/static/src/main.vala
diff --git a/xmake/repository/templates/vala/static/src/mymath.vala b/xmake/templates/vala/static/src/mymath.vala
index 4f4e761c5..4f4e761c5 100644
--- a/xmake/repository/templates/vala/static/src/mymath.vala
+++ b/xmake/templates/vala/static/src/mymath.vala
diff --git a/xmake/repository/templates/vala/static/xmake.lua b/xmake/templates/vala/static/xmake.lua
index 8e2430db7..8e2430db7 100644
--- a/xmake/repository/templates/vala/static/xmake.lua
+++ b/xmake/templates/vala/static/xmake.lua
diff --git a/xmake/repository/templates/zig/console/src/main.zig b/xmake/templates/zig/console/src/main.zig
index db34441e1..db34441e1 100644
--- a/xmake/repository/templates/zig/console/src/main.zig
+++ b/xmake/templates/zig/console/src/main.zig
diff --git a/xmake/repository/templates/zig/console/xmake.lua b/xmake/templates/zig/console/xmake.lua
index 28940257d..28940257d 100644
--- a/xmake/repository/templates/zig/console/xmake.lua
+++ b/xmake/templates/zig/console/xmake.lua
diff --git a/xmake/repository/templates/zig/shared/src/main.zig b/xmake/templates/zig/shared/src/main.zig
index 7dc7b2bc4..7dc7b2bc4 100644
--- a/xmake/repository/templates/zig/shared/src/main.zig
+++ b/xmake/templates/zig/shared/src/main.zig
diff --git a/xmake/repository/templates/zig/shared/src/test.zig b/xmake/templates/zig/shared/src/test.zig
index a04ec1544..a04ec1544 100644
--- a/xmake/repository/templates/zig/shared/src/test.zig
+++ b/xmake/templates/zig/shared/src/test.zig
diff --git a/xmake/repository/templates/zig/shared/xmake.lua b/xmake/templates/zig/shared/xmake.lua
index 2577bde9f..2577bde9f 100644
--- a/xmake/repository/templates/zig/shared/xmake.lua
+++ b/xmake/templates/zig/shared/xmake.lua
diff --git a/xmake/repository/templates/zig/static/src/main.zig b/xmake/templates/zig/static/src/main.zig
index 7dc7b2bc4..7dc7b2bc4 100644
--- a/xmake/repository/templates/zig/static/src/main.zig
+++ b/xmake/templates/zig/static/src/main.zig
diff --git a/xmake/repository/templates/zig/static/src/test.zig b/xmake/templates/zig/static/src/test.zig
index a04ec1544..a04ec1544 100644
--- a/xmake/repository/templates/zig/static/src/test.zig
+++ b/xmake/templates/zig/static/src/test.zig
diff --git a/xmake/repository/templates/zig/static/xmake.lua b/xmake/templates/zig/static/xmake.lua
index 2cc6601d2..2cc6601d2 100644
--- a/xmake/repository/templates/zig/static/xmake.lua
+++ b/xmake/templates/zig/static/xmake.lua
diff --git a/xmake/toolchains/mingw/xmake.lua b/xmake/toolchains/mingw/xmake.lua
index d0695dcfe..a75ab3cce 100644
--- a/xmake/toolchains/mingw/xmake.lua
+++ b/xmake/toolchains/mingw/xmake.lua
@@ -22,7 +22,7 @@ toolchain("mingw")
set_kind("standalone")
set_homepage("http://www.mingw.org/")
set_description("Minimalist GNU for Windows")
- set_runtimes("stdc++_static", "stdc++_shared")
+ set_runtimes("stdc++_static", "stdc++_shared", "c++_static", "c++_shared")
on_check("check")
on_load(function (toolchain)