Browse Source

boost: cmake test (#5640)

* boost: cmake test

* fix windows build

* move test to single file

* add iostreams support

* support regex, locale

* use tar.gz

* support locale, python

* Use sorted libs

* support all buildable by default configs

* support all configs

* support header only config

* improve all config

* improve header only config

* add warning for mpi config

* merge to boost package

* workaround for cmake url

* boostdep: add package

* hack add_urls

* boostdep: fix c++ language

* improve xmake.lua
star9029 10 months ago
parent
commit
ba8fd3c1a5

+ 269 - 0
packages/b/boost/b2/install.lua

@@ -0,0 +1,269 @@
+import("core.tool.toolchain")
+import("core.base.option")
+
+function _get_compiler(package, toolchain)
+    local cxx = package:build_getenv("cxx")
+    if package:is_plat("macosx") then
+        -- we uses ld/clang++ for link stdc++ for shared libraries
+        -- and we need `xcrun -sdk macosx clang++` to make b2 to get `-isysroot` automatically
+        local cc = package:build_getenv("ld")
+        if cc and cc:find("clang", 1, true) and cc:find("Xcode", 1, true) then
+            cc = "xcrun -sdk macosx clang++"
+        end
+        return format("using darwin : : %s ;", cc)
+    elseif package:is_plat("windows") then
+        local vs_toolset = toolchain:config("vs_toolset")
+        local msvc_ver = ""
+        local win_toolset = "msvc"
+        if toolchain:name() == "clang-cl" then
+            win_toolset = "clang-win"
+            cxx = cxx:gsub("(clang%-cl)$", "%1.exe", 1)
+            msvc_ver = ""
+        elseif vs_toolset then
+            local i = vs_toolset:find("%.")
+            msvc_ver = i and vs_toolset:sub(1, i + 1)
+        end
+
+        -- Specifying a version will disable b2 from forcing tools
+        -- from the latest installed msvc version.
+        return format("using %s : %s : \"%s\" ;", win_toolset, msvc_ver, cxx:gsub("\\", "\\\\"))
+    else
+        cxx = cxx:gsub("gcc$", "g++")
+        cxx = cxx:gsub("gcc%-", "g++-")
+        cxx = cxx:gsub("clang$", "clang++")
+        cxx = cxx:gsub("clang%-", "clang++-")
+        if cxx and cxx:find("clang", 1, true) then
+            return format("using clang : : \"%s\" ;", cxx:gsub("\\", "/"))
+        else
+            return format("using gcc : : \"%s\" ;", cxx:gsub("\\", "/"))
+        end
+    end
+end
+
+function _config_deppath(file, depname, rule)
+    local dep = package:dep(depname)
+    local info = dep:fetch({external = false})
+    if info then
+        local includedirs = table.wrap(info.sysincludedirs or info.includedirs)
+        for i, dir in ipairs(includedirs) do
+            includedirs[i] = path.unix(dir)
+        end
+        local linkdirs = table.wrap(info.linkdirs)
+        for i, dir in ipairs(linkdirs) do
+            linkdirs[i] = path.unix(dir)
+        end
+        local links = table.wrap(info.links)
+        local usingstr = format("\nusing %s : %s : <include>%s <search>%s <name>%s ;",
+            rule, dep:version(),
+            table.concat(includedirs, ";"),
+            table.concat(linkdirs, ";"),
+            table.concat(links, ";"))
+        file:write(usingstr)
+    end
+end
+
+function main(package)
+    import("libs", {rootdir = package:scriptdir()})
+
+    -- get host toolchain
+    local host_toolchain
+    if package:is_plat("windows") then
+        host_toolchain = toolchain.load("msvc", {plat = "windows", arch = os.arch()})
+        if not host_toolchain:check() then
+            host_toolchain = toolchain.load("clang-cl", {plat = "windows", arch = os.arch()})
+        end
+        assert(host_toolchain:check(), "host msvc or clang-cl not found!")
+    end
+
+    -- force boost to compile with the desired compiler
+    local file = io.open("user-config.jam", "w")
+    if file then
+        file:write(_get_compiler(package, host_toolchain))
+        file:close()
+    end
+
+    local bootstrap_argv =
+    {
+        "--prefix=" .. package:installdir(),
+        "--libdir=" .. package:installdir("lib"),
+        "--without-icu"
+    }
+
+    if package:has_tool("cxx", "clang", "clangxx") then
+        table.insert(bootstrap_argv, "--with-toolset=clang")
+    end
+
+    if package:is_plat("windows") then
+        -- for bootstrap.bat, all other arguments are useless
+        bootstrap_argv = { "msvc" }
+        os.vrunv("bootstrap.bat", bootstrap_argv, {envs = host_toolchain:runenvs()})
+    elseif package:is_plat("mingw") and is_host("windows") then
+        bootstrap_argv = { "gcc" }
+        os.vrunv("bootstrap.bat", bootstrap_argv)
+        -- todo looking for better solution to fix the confict between user-config.jam and project-config.jam
+        io.replace("project-config.jam", "using[^\n]+", "")
+    else
+        os.vrunv("./bootstrap.sh", bootstrap_argv)
+    end
+
+    -- get build toolchain
+    local build_toolchain
+    local build_toolset
+    local runenvs
+    if package:is_plat("windows") then
+        if package:has_tool("cxx", "clang_cl") then
+            build_toolset = "clang-win"
+            build_toolchain = package:toolchain("clang-cl")
+        elseif package:has_tool("cxx", "clang") then
+            build_toolset = "clang-win"
+            build_toolchain = package:toolchain("clang") or package:toolchain("llvm")
+        elseif package:has_tool("cxx", "cl") then
+            build_toolset = "msvc"
+            build_toolchain = package:toolchain("msvc")
+        end
+        if build_toolchain then
+            runenvs = build_toolchain:runenvs()
+        end
+    end
+
+    local file = io.open("user-config.jam", "w")
+    if file then
+        file:write(_get_compiler(package, build_toolchain))
+        if package:config("lzma") then
+            _config_deppath(file, "xz", "lzma")
+        end
+        if package:config("zstd") then
+            _config_deppath(file, "zstd", "zstd")
+        end
+        if package:config("zlib") then
+            _config_deppath(file, "zlib", "zlib")
+        end
+        if package:config("bzip2") then
+            _config_deppath(file, "bzip2", "bzip2")
+        end
+        file:close()
+    end
+    os.vrun("./b2 headers")
+
+    local njobs = option.get("jobs") or tostring(os.default_njob())
+    local argv =
+    {
+        "--prefix=" .. package:installdir(),
+        "--libdir=" .. package:installdir("lib"),
+        "-d2",
+        "-j" .. njobs,
+        "--hash",
+        "-q", -- quit on first error
+        "--layout=tagged-1.66", -- prevent -x64 suffix in case cmake can't find it
+        "--user-config=user-config.jam",
+        "install",
+        "threading=" .. (package:config("multi") and "multi" or "single"),
+        "debug-symbols=" .. (package:debug() and "on" or "off"),
+        "link=" .. (package:config("shared") and "shared" or "static"),
+        "variant=" .. (package:is_debug() and "debug" or "release"),
+        "runtime-debugging=" .. (package:is_debug() and "on" or "off")
+    }
+
+    local cxxflags = {}
+    if package:config("lzma") then
+        if package:is_plat("windows") and not package:dep("xz"):config("shared") then
+            table.insert(cxxflags, "-DLZMA_API_STATIC")
+        end
+    else
+        table.insert(argv, "-sNO_LZMA=1")
+    end
+    if not package:config("zstd") then
+        table.insert(argv, "-sNO_ZSTD=1")
+    end
+    if not package:config("zlib") then
+        table.insert(argv, "-sNO_ZLIB=1")
+    end
+    if not package:config("bzip2") then
+        table.insert(argv, "-sNO_BZIP2=1")
+    end
+
+    if package:config("lto") then
+        table.insert(argv, "lto=on")
+    end
+    if package:is_arch("aarch64", "arm+.*") then
+        table.insert(argv, "architecture=arm")
+    end
+    if package:is_arch(".+64.*") then
+        table.insert(argv, "address-model=64")
+    else
+        table.insert(argv, "address-model=32")
+    end
+
+    local linkflags = {}
+    table.join2(cxxflags, table.wrap(package:config("cxflags")))
+    table.join2(cxxflags, table.wrap(package:config("cxxflags")))
+    if package:is_plat("windows") then
+        if package:config("shared") then
+            table.insert(argv, "runtime-link=shared")
+        elseif package:has_runtime("MT", "MTd") then
+            table.insert(argv, "runtime-link=static")
+        else
+            table.insert(argv, "runtime-link=shared")
+        end
+        table.insert(argv, "toolset=" .. build_toolset)
+        table.insert(cxxflags, "-std:c++14")
+    elseif package:is_plat("mingw") then
+        table.insert(argv, "toolset=gcc")
+    elseif package:is_plat("macosx") then
+        table.insert(argv, "toolset=darwin")
+
+        -- fix macosx arm64 build issue https://github.com/microsoft/vcpkg/pull/18529
+        table.insert(cxxflags, "-std=c++14")
+        table.insert(cxxflags, "-arch")
+        table.insert(cxxflags, package:arch())
+        local xcode = package:toolchain("xcode") or import("core.tool.toolchain").load("xcode", {plat = package:plat(), arch = package:arch()})
+        if xcode:check() then
+            local xcode_dir = xcode:config("xcode")
+            local xcode_sdkver = xcode:config("xcode_sdkver")
+            local target_minver = xcode:config("target_minver")
+            if xcode_dir and xcode_sdkver then
+                local xcode_sdkdir = xcode_dir .. "/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX" .. xcode_sdkver .. ".sdk"
+                table.insert(cxxflags, "-isysroot")
+                table.insert(cxxflags, xcode_sdkdir)
+            end
+            if target_minver then
+                table.insert(cxxflags, "-mmacosx-version-min=" .. target_minver)
+            end
+        end
+    else
+        table.insert(cxxflags, "-std=c++14")
+        if package:config("pic") ~= false then
+            table.insert(cxxflags, "-fPIC")
+        end
+    end
+    if package.has_runtime and package:has_runtime("c++_shared", "c++_static") then
+        table.insert(cxxflags, "-stdlib=libc++")
+        table.insert(linkflags, "-stdlib=libc++")
+        if package:has_runtime("c++_static") then
+            table.insert(linkflags, "-static-libstdc++")
+        end
+    end
+    if package:config("asan") then
+        table.insert(cxxflags, "-fsanitize=address")
+        table.insert(linkflags, "-fsanitize=address")
+    end
+    if cxxflags then
+        table.insert(argv, "cxxflags=" .. table.concat(cxxflags, " "))
+    end
+    if linkflags then
+        table.insert(argv, "linkflags=" .. table.concat(linkflags, " "))
+    end
+    libs.for_each(function (libname)
+        if package:config("all") or package:config(libname) then
+            table.insert(argv, "--with-" .. libname)
+        end
+    end)
+
+    if package:is_plat("linux") then
+        table.insert(argv, "pch=off")
+    end
+    local ok = os.execv("./b2", argv, {envs = runenvs, try = true, stdout = "boost-log.txt"})
+    if ok ~= 0 then
+        raise("boost build failed, please check log in " .. path.join(os.curdir(), "boost-log.txt"))
+    end
+end

+ 94 - 0
packages/b/boost/b2/load.lua

@@ -0,0 +1,94 @@
+function _get_linkname(package, libname)
+    local linkname
+    if package:is_plat("windows") then
+        linkname = (package:config("shared") and "boost_" or "libboost_") .. libname
+    else
+        linkname = "boost_" .. libname
+    end
+    if libname == "python" or libname == "numpy" then
+        linkname = linkname .. package:config("pyver"):gsub("%p+", "")
+    end
+    if package:config("multi") then
+        linkname = linkname .. "-mt"
+    end
+    if package:is_plat("windows") then
+        if package:config("shared") then
+            if package:debug() then
+                linkname = linkname .. "-gd"
+            end
+        elseif package:config("asan") or package:has_runtime("MTd") then
+            linkname = linkname .. "-sgd"
+        elseif package:has_runtime("MT") then
+            linkname = linkname .. "-s"
+        elseif package:config("asan") or package:has_runtime("MDd") then
+            linkname = linkname .. "-gd"
+        end
+    else
+        if package:debug() then
+            linkname = linkname .. "-d"
+        end
+    end
+    return linkname
+end
+
+function main(package)
+    import("libs", {rootdir = package:scriptdir()})
+
+    -- we need the fixed link order
+    local headeronly = not package:config("all")
+    local sublibs = {log = {"log_setup", "log"},
+                    python = {"python", "numpy"},
+                    stacktrace = {"stacktrace_backtrace", "stacktrace_basic"}}
+
+    libs.for_each(function (libname)
+        if package:config(libname) then
+            headeronly = false
+        end
+        local libs = sublibs[libname]
+        if libs then
+            for _, lib in ipairs(libs) do
+                package:add("links", _get_linkname(package, lib))
+            end
+        else
+            package:add("links", _get_linkname(package, libname))
+        end
+    end)
+
+    if headeronly then
+        package:set("kind", "library", {headeronly = true})
+    end
+    -- disable auto-link all libs
+    if package:is_plat("windows") then
+        package:add("defines", "BOOST_ALL_NO_LIB")
+    end
+
+    if package:config("python") then
+        if not package:config("shared") then
+            package:add("defines", "BOOST_PYTHON_STATIC_LIB")
+        end
+        package:add("deps", "python " .. package:config("pyver") .. ".x", {configs = {headeronly = true}})
+    end
+    if package:config("zstd") then
+        package:add("deps", "zstd")
+    end
+    if package:config("lzma") then
+        package:add("deps", "xz")
+    end
+    if package:config("zlib") then
+        package:add("deps", "zlib")
+    end
+    if package:config("bzip2") then
+        package:add("deps", "bzip2")
+    end
+
+    if package:is_plat("windows") and package:version():le("1.85.0") then
+        local vs_toolset = package:toolchain("msvc"):config("vs_toolset")
+        if vs_toolset then
+            local vs_toolset_ver = import("core.base.semver").new(vs_toolset)
+            local minor = vs_toolset_ver:minor()
+            if minor and minor >= 40 then
+                package:add("patches", "<=1.85.0", "patches/1.85.0/fix-v144.patch", "1ba99cb2e2f03a4ba489a32596c62e1310b6c73ba4d19afa8796bcf180c84422")
+            end
+        end
+    end
+end

+ 160 - 0
packages/b/boost/cmake/install.lua

@@ -0,0 +1,160 @@
+import("core.base.hashset")
+import("core.base.option")
+
+function _mangle_link_string(package)
+    local link = "boost_"
+    if package:is_plat("windows") and not package:config("shared") then
+        link = "lib" .. link
+    end
+    return link
+end
+-- Only get package dep version in on_install
+function _add_links(package)
+    local suffix = _mangle_link_string(package)
+
+    local sub_libs_map = {
+        test = {"prg_exec_monitor", "unit_test_framework"},
+        serialization = {"wserialization", "serialization"},
+        fiber = {"fiber", "fiber_numa"},
+        log = {"log", "log_setup"},
+        stacktrace = {
+            "stacktrace_noop",
+            "stacktrace_backtrace",
+            "stacktrace_addr2line",
+            "stacktrace_basic",
+            "stacktrace_windbg",
+            "stacktrace_windbg_cached",
+        },
+    }
+
+    if package:config("python") then
+        local py_ver = assert(package:dep("python"):version(), "Can't get python version")
+        py_ver = py_ver:major() .. py_ver:minor()
+        -- TODO: detect numpy
+        sub_libs_map["python"] = {
+            "python" .. py_ver,
+            "numpy" .. py_ver,
+        }
+    end
+
+    libs.for_each(function (libname)
+        if not package:config(libname) then
+            return
+        end
+
+        local sub_libs = sub_libs_map[libname]
+        if sub_libs then
+            for _, sub_libname in ipairs(sub_libs) do
+                package:add("links", suffix .. sub_libname)
+            end
+            if libname == "test" then
+                -- always static
+                package:add("links", "libboost_test_exec_monitor")
+            end
+        else
+            package:add("links", suffix .. libname)
+        end
+    end)
+end
+
+function _check_links(package)
+    local lib_files = {}
+    local links = hashset.from(table.wrap(package:get("links")))
+
+    for _, libfile in ipairs(os.files(package:installdir("lib/*"))) do
+        local link = path.basename(libfile)
+        if not links:remove(link) then
+            table.insert(lib_files, path.filename(libfile))
+        end
+    end
+
+    links = links:to_array()
+    if #links ~= 0 then
+        -- TODO: Remove header only "link" or unsupported platform link
+        wprint("Missing library files\n" .. table.concat(links, "\n"))
+    end
+    if #lib_files ~= 0 then
+        wprint("Missing links\n" .. table.concat(lib_files, "\n"))
+    end
+end
+
+function _add_iostreams_configs(package, configs)
+    local iostreams_deps = {"zlib", "bzip2", "lzma", "zstd"}
+    for _, dep in ipairs(iostreams_deps) do
+        local config = format("-DBOOST_IOSTREAMS_ENABLE_%s=%s", dep:upper(), (package:config(dep) and "ON" or "OFF"))
+        table.insert(configs, config)
+    end
+end
+
+function _add_libs_configs(package, configs)
+    if not package:config("all") then
+        local header_only_buildable
+        if package:is_headeronly() then
+            header_only_buildable = hashset.from(libs.get_header_only_buildable())
+        end
+
+        local exclude_libs = {}
+        libs.for_each(function (libname)
+            if header_only_buildable and header_only_buildable:has(libname) then
+                -- continue
+            else
+                if not package:config(libname) then
+                    table.insert(exclude_libs, libname)
+                end
+            end
+        end)
+        table.insert(configs, "-DBOOST_EXCLUDE_LIBRARIES=" .. table.concat(exclude_libs, ";"))
+    end
+
+    table.insert(configs, "-DBOOST_ENABLE_PYTHON=" .. (package:config("python") and "ON" or "OFF"))
+    table.insert(configs, "-DBOOST_ENABLE_MPI=" .. (package:config("mpi") and "ON" or "OFF"))
+    if package:config("locale") then
+        table.insert(configs, "-DCMAKE_CXX_STANDARD=17")
+    end
+
+    _add_iostreams_configs(package, configs)
+
+    local openssl = package:dep("openssl")
+    if openssl and not openssl:is_system() then
+        table.insert(configs, "-DOPENSSL_ROOT_DIR=" .. openssl:installdir())
+    end
+end
+
+function _add_opt(package, opt)
+    opt.cxflags = {}
+    local lzma = package:dep("xz")
+    if lzma and not lzma:config("shared") then
+        table.insert(opt.cxflags, "-DLZMA_API_STATIC")
+    end
+    
+    if package:has_tool("cxx", "cl") then
+        table.insert(opt.cxflags, "/EHsc")
+    end
+end
+
+function main(package)
+    import("libs", {rootdir = package:scriptdir()})
+
+    local configs = {"-DBOOST_INSTALL_LAYOUT=system"}
+    table.insert(configs, "-DCMAKE_BUILD_TYPE=" .. (package:is_debug() and "Debug" or "Release"))
+    table.insert(configs, "-DBUILD_SHARED_LIBS=" .. (package:config("shared") and "ON" or "OFF"))
+    if package:is_plat("windows") then
+        table.insert(configs, "-DCMAKE_COMPILE_PDB_OUTPUT_DIRECTORY=''")
+    end
+
+    _add_libs_configs(package, configs)
+
+    if option.get("verbose") then
+        table.insert(configs, "-DBoost_DEBUG=ON")
+    end
+
+    local opt = {}
+    _add_opt(package, opt)
+    import("package.tools.cmake").install(package, configs, opt)
+
+    _add_links(package)
+
+    if option.get("verbose") then
+        _check_links(package)
+    end
+end

+ 112 - 0
packages/b/boost/cmake/load.lua

@@ -0,0 +1,112 @@
+function _add_defines(package)
+    if package:is_plat("windows") then
+        package:add("defines", "BOOST_ALL_NO_LIB")
+    end
+    if package:config("shared") then
+        package:add("defines", "BOOST_ALL_DYN_LINK")
+    end
+end
+
+function _recursion_enabled_dep_configs(package, libname, deps, visited_table)
+    if package:config(libname) and not visited_table[libname] then
+        visited_table[libname] = true
+        for _, dep_libname in ipairs(deps) do
+            package:config_set(dep_libname, true)
+            _recursion_enabled_dep_configs(package, dep_libname, libs.get_lib_deps()[dep_libname], visited_table)
+        end
+    end
+end
+
+function _auto_enabled_dep_configs(package)
+    -- workaround
+    if package:config("locale") then
+        package:config_set("regex", true)
+    end
+    if package:config("python") then
+        package:config_set("thread", true)
+    end
+
+    local visited_table = {}
+
+    libs.for_each_lib_deps(function (libname, deps)
+        _recursion_enabled_dep_configs(package, libname, deps, visited_table)
+    end)
+end
+
+function _add_iostreams_deps(package)
+    if not package:config("iostreams") then
+        return
+    end
+
+    if package:config("zlib") then
+        package:add("deps", "zlib")
+    end
+    if package:config("bzip2") then
+        package:add("deps", "bzip2")
+    end
+    if package:config("lzma") then
+        package:add("deps", "xz")
+    end
+
+    if package:config("zstd") then
+        package:add("deps", "zstd")
+
+        package:add("deps", (is_subhost("windows") and "pkgconf") or "pkg-config")
+        package:add("patches", "1.86.0", "patches/1.86.0/find-zstd.patch", "7a90f2cbf01fc26bc8a98d58468c20627974f30e45bdd4a00c52644b60af1ef6")
+    end
+end
+
+function _add_deps(package)
+    if package:config("regex") then
+        package:add("deps", "icu4c")
+    end
+    if package:config("locale") then
+        package:add("deps", "libiconv", "icu4c")
+    end
+    if package:config("python") then
+        package:add("deps", "python", {configs = {headeronly = true}})
+    end
+    if package:config("openssl") then
+        package:add("deps", "openssl >=1.1.1-a") -- same as python on_load
+    end
+
+    _add_iostreams_deps(package)
+end
+
+function _add_header_only_configs(package)
+    libs.for_each(function (libname)
+        package:config_set(libname, false)
+    end)
+    -- TODO: find cmake option to install header only library
+    -- libs.for_each_header_only_buildable_lib(function (libname)
+    --     package:config_set(libname, true)
+    -- end)
+end
+
+function main(package)
+    import("libs", {rootdir = package:scriptdir()})
+
+    if package:config("header_only") then
+        package:set("kind", "library", {headeronly = true})
+        _add_header_only_configs(package)
+    else
+        if package:config("all") then
+            package:config_set("openssl", true) -- mysql/redis require
+            libs.for_each(function (libname)
+                package:config_set(libname, true)
+            end)
+        else
+            _auto_enabled_dep_configs(package)
+        end
+    end
+
+    if package:config("mpi") then
+        -- TODO: add mpi to xrepo
+        package:config_set("mpi", false)
+        wprint("package(boost) Unsupported mpi config")
+    end
+
+    _add_deps(package)
+
+    _add_defines(package)
+end

+ 191 - 0
packages/b/boost/libs.lua

@@ -0,0 +1,191 @@
+local sorted_libs = {
+  "wave",
+  "url",
+  "type_erasure",
+  "timer",
+  "test",
+  "stacktrace",
+  "program_options",
+  "process",
+  "nowide",
+  "log",
+  "locale",
+  "json",
+  "iostreams",
+  "graph_parallel",
+  "mpi",
+  "python",
+  "graph",
+  "serialization",
+  "regex",
+  "math",
+  "random",
+  "fiber",
+  "filesystem",
+  "coroutine",
+  "contract",
+  "thread",
+  "date_time",
+  "exception",
+  "cobalt",
+  "context",
+  "container",
+  "chrono",
+  "system",
+  "charconv",
+  "atomic"
+}
+
+local libs_dep = {
+  json = {
+    "container",
+    "system"
+  },
+  python = {
+    "graph"
+  },
+  test = {
+    "exception"
+  },
+  type_erasure = {
+    "thread"
+  },
+  thread = {
+    "atomic",
+    "chrono",
+    "container",
+    "date_time",
+    "exception",
+    "system"
+  },
+  fiber = {
+    "context",
+    "filesystem"
+  },
+  chrono = {
+    "system"
+  },
+  charconv = { },
+  contract = {
+    "exception",
+    "thread"
+  },
+  timer = { },
+  wave = {
+    "filesystem",
+    "serialization"
+  },
+  stacktrace = { },
+  coroutine = {
+    "context",
+    "exception",
+    "system"
+  },
+  math = {
+    "random"
+  },
+  exception = { },
+  filesystem = {
+    "atomic",
+    "system"
+  },
+  date_time = { },
+  atomic = { },
+  url = {
+    "system"
+  },
+  serialization = { },
+  process = {
+    "filesystem",
+    "system"
+  },
+  regex = { },
+  container = { },
+  random = {
+    "system"
+  },
+  nowide = {
+    "filesystem"
+  },
+  program_options = { },
+  system = { },
+  cobalt = {
+    "container",
+    "context",
+    "system"
+  },
+  graph = {
+    "math",
+    "random",
+    "regex",
+    "serialization"
+  },
+  context = { },
+  mpi = {
+    "graph",
+    "python",
+    "serialization"
+  },
+  log = {
+    "atomic",
+    "date_time",
+    "exception",
+    "filesystem",
+    "random",
+    "regex",
+    "system",
+    "thread"
+  },
+  iostreams = {
+    "random",
+    "regex"
+  },
+  locale = {
+    "thread"
+  },
+  graph_parallel = {
+    "filesystem",
+    "graph",
+    "mpi",
+    "random",
+    "serialization"
+  }
+}
+
+local header_only_buildable = {
+  "graph_parallel",
+  "system",
+  "exception",
+  "regex",
+  "math",
+}
+
+function get_libs()
+    return sorted_libs
+end
+
+function get_lib_deps()
+    return libs_dep
+end
+
+function get_header_only_buildable()
+    return header_only_buildable
+end
+
+function for_each(lambda)
+    for _, libname in ipairs(get_libs()) do
+        lambda(libname)
+    end
+end
+
+function for_each_header_only_buildable_lib(lambda)
+    for _, libname in ipairs(get_header_only_buildable()) do
+        lambda(libname)
+    end
+end
+
+function for_each_lib_deps(lambda)
+    for libname, deps in pairs(get_lib_deps()) do
+        lambda(libname, deps)
+    end
+end

+ 19 - 0
packages/b/boost/patches/1.86.0/find-zstd.patch

@@ -0,0 +1,19 @@
+diff --git a/libs/iostreams/CMakeLists.txt b/libs/iostreams/CMakeLists.txt
+index 1d8352f..01b612c 100644
+--- a/libs/iostreams/CMakeLists.txt
++++ b/libs/iostreams/CMakeLists.txt
+@@ -27,7 +27,13 @@ function(boost_iostreams_option name description package version found target) #
+ 
+   if(${name})
+ 
+-    find_package(${package} ${version} REQUIRED)
++    if("${package}" STREQUAL "zstd")
++      include(FindPkgConfig)
++      pkg_search_module("libzstd" REQUIRED IMPORTED_TARGET "libzstd")
++      set(target "PkgConfig::libzstd")
++    else()
++      find_package(${package} ${version} REQUIRED)
++    endif()
+     target_sources(boost_iostreams PRIVATE ${ARGN})
+     target_link_libraries(boost_iostreams PRIVATE ${target})
+ 

+ 107 - 0
packages/b/boost/test.lua

@@ -0,0 +1,107 @@
+function _iostreams(package, snippets)
+    if not package:config("iostreams") then
+        return
+    end
+
+    if package:config("zstd") then
+        table.insert(snippets,
+            [[
+                #include <boost/iostreams/filter/zstd.hpp>
+                #include <boost/iostreams/filtering_stream.hpp>
+                void test() {
+                    boost::iostreams::filtering_ostream out;
+                    out.push(boost::iostreams::zstd_compressor());
+                }
+            ]]
+        )
+    end
+
+    if package:config("lzma") then
+        table.insert(snippets,
+            [[
+                #include <boost/iostreams/filter/lzma.hpp>
+                #include <boost/iostreams/filtering_stream.hpp>
+                void test() {
+                    boost::iostreams::filtering_ostream out;
+                    out.push(boost::iostreams::lzma_compressor());
+                }
+            ]]
+        )
+    end
+end
+
+function _filesystem(package, snippets)
+    if package:config("filesystem") then
+        table.insert(snippets,
+            [[
+                #include <boost/filesystem.hpp>
+                #include <iostream>
+                void test() {
+                    boost::filesystem::path path("/path/to/directory");
+                    if (boost::filesystem::exists(path)) {
+                        std::cout << "Directory exists" << std::endl;
+                    } else {
+                        std::cout << "Directory does not exist" << std::endl;
+                    }
+                }
+            ]]
+        )
+    end
+end
+
+function _date_time(package, snippets)
+    if package:config("date_time") then
+        table.insert(snippets,
+            [[
+                #include <boost/date_time/gregorian/gregorian.hpp>
+                void test() {
+                    boost::gregorian::date d(2010, 1, 30);
+                }
+            ]]
+        )
+    end
+end
+
+function _header_only(package, snippets)
+    table.insert(snippets,
+        [[
+            #include <boost/algorithm/string.hpp>
+            #include <string>
+            #include <vector>
+            void test() {
+                std::string str("a,b");
+                std::vector<std::string> vec;
+                boost::algorithm::split(vec, str, boost::algorithm::is_any_of(","));
+            }
+        ]]
+    )
+    table.insert(snippets,
+        [[
+            #include <boost/unordered_map.hpp>
+            void test() {
+                boost::unordered_map<std::string, int> map;
+                map["2"] = 2;
+            }
+        ]]
+    )
+end
+
+function main(package)
+    local snippets = {}
+
+    if package:config("header_only") then
+        _header_only(package, snippets)
+    else
+        if not package:config("cmake") then
+            _header_only(package, snippets)
+        end
+        _iostreams(package, snippets)
+        _filesystem(package, snippets)
+        _date_time(package, snippets)
+    end
+
+    local opt = {configs = {languages = "c++14"}}
+    for _, snippet in ipairs(snippets) do
+        assert(package:check_cxxsnippets({test = snippet}, opt))
+    end
+end

+ 53 - 461
packages/b/boost/xmake.lua

@@ -3,18 +3,24 @@ package("boost")
     set_description("Collection of portable C++ source libraries.")
     set_license("BSL-1.0")
 
+    -- xrepo does not support `package:config("cmake")` in on_source to set the download url, so if you want to build with cmake, we need to `add_urls` cmake archive url at first line.
+    -- Users can also download the cmake archive and put it in `xmake g --pkg_searchdirs=` to avoid xrepo using a non-cmake archive url.
+    add_urls("https://github.com/boostorg/boost/releases/download/boost-$(version)/boost-$(version)-cmake.tar.gz", {alias = "cmake"})
     add_urls("https://github.com/boostorg/boost/releases/download/boost-$(version)/boost-$(version)-b2-nodocs.tar.gz")
     add_urls("https://github.com/boostorg/boost/releases/download/boost-$(version)/boost-$(version).tar.gz")
     add_urls("https://github.com/xmake-mirror/boost/releases/download/boost-$(version).tar.bz2", {alias = "mirror", version = function (version)
             return version .. "/boost_" .. (version:gsub("%.", "_"))
         end})
 
+    add_versions("cmake:1.86.0", "c62ce6e64d34414864fef946363db91cea89c1b90360eabed0515f0eda74c75c")
+
     add_versions("1.86.0", "2128a4c96862b5c0970c1e34d76b1d57e4a1016b80df85ad39667f30b1deba26")
     add_versions("1.85.0", "f4a7d3f81b8a0f65067b769ea84135fd7b72896f4f59c7f405086c8c0dc61434")
     add_versions("1.84.0", "4d27e9efed0f6f152dc28db6430b9d3dfb40c0345da7342eaa5a987dde57bd95")
     add_versions("1.83.0", "0c6049764e80aa32754acd7d4f179fd5551d8172a83b71532ae093e7384e98da")
     add_versions("1.82.0", "b62bd839ea6c28265af9a1f68393eda37fab3611425d3b28882d8e424535ec9d")
     add_versions("1.81.0", "121da556b718fd7bd700b5f2e734f8004f1cfa78b7d30145471c526ba75a151c")
+
     add_versions("mirror:1.80.0", "1e19565d82e43bc59209a168f5ac899d3ba471d55c7610c677d4ccf2c9c500c0")
     add_versions("mirror:1.79.0", "475d589d51a7f8b3ba2ba4eda022b170e562ca3b760ee922c146b6c65856ef39")
     add_versions("mirror:1.78.0", "8681f175d4bdb26c52222665793eef08490d7758529330f98d3b29dd0735bccc")
@@ -26,6 +32,25 @@ package("boost")
     add_versions("mirror:1.72.0", "59c9b274bc451cf91a9ba1dd2c7fdcaf5d60b1b3aa83f2c9fa143417cc660722")
     add_versions("mirror:1.70.0", "430ae8354789de4fd19ee52f3b1f739e1fba576f0aded0897c3c2bc00fb38778")
 
+    add_patches("1.75.0", "patches/1.75.0/warning.patch", "43ff97d338c78b5c3596877eed1adc39d59a000cf651d0bcc678cf6cd6d4ae2e")
+
+    includes(path.join(os.scriptdir(), "libs.lua"))
+    for _, libname in ipairs(get_libs()) do
+        add_configs(libname, {description = "Enable " .. libname .. " library.", default = (libname == "filesystem"), type = "boolean"})
+    end
+    add_configs("zlib", {description = "Enable zlib for iostreams", default = false, type = "boolean"})
+    add_configs("bzip2", {description = "Enable bzip2 for iostreams", default = false, type = "boolean"})
+    add_configs("lzma", {description = "Enable lzma for iostreams", default = false, type = "boolean"})
+    add_configs("zstd", {description = "Enable zstd for iostreams", default = false, type = "boolean"})
+    add_configs("openssl", {description = "Enable openssl for mysql/redis", default = false, type = "boolean"})
+
+    add_configs("cmake", {description = "Use cmake build system", default = true, type = "boolean"})
+    add_configs("all", {description = "Enable all library modules support.", default = false, type = "boolean"})
+    add_configs("header_only", {description = "Enable header only modules", default = false, type = "boolean"})
+
+    add_configs("pyver", {description = "python version x.y, etc. 3.10 (only for b2)", default = "3.10"})
+    add_configs("multi", {description = "Enable multi-thread support (only for b2)",  default = true, type = "boolean"})
+
     if is_plat("mingw") and is_subhost("msys") then
         add_extsources("pacman::boost")
     elseif is_plat("linux") then
@@ -34,483 +59,50 @@ package("boost")
         add_extsources("brew::boost")
     end
 
-    add_patches("1.75.0", path.join(os.scriptdir(), "patches", "1.75.0", "warning.patch"), "43ff97d338c78b5c3596877eed1adc39d59a000cf651d0bcc678cf6cd6d4ae2e")
-
-    if is_plat("linux") then
-        add_deps("bzip2", "zlib")
+    if is_plat("linux", "bsd") then
         add_syslinks("pthread", "dl")
     end
 
-    add_configs("pyver", {description = "python version x.y, etc. 3.10", default = "3.10"})
-    local libnames = {"atomic",
-                      "charconv",
-                      "chrono",
-                      "cobalt",
-                      "container",
-                      "context",
-                      "contract",
-                      "coroutine",
-                      "date_time",
-                      "exception",
-                      "fiber",
-                      "filesystem",
-                      "graph",
-                      "graph_parallel",
-                      "headers",
-                      "iostreams",
-                      "json",
-                      "locale",
-                      "log",
-                      "math",
-                      "mpi",
-                      "nowide",
-                      "program_options",
-                      "python",
-                      "random",
-                      "regex",
-                      "serialization",
-                      "stacktrace",
-                      "system",
-                      "test",
-                      "thread",
-                      "timer",
-                      "type_erasure",
-                      "url",
-                      "wave"}
-
-    add_configs("all",          { description = "Enable all library modules support.",  default = false, type = "boolean"})
-    add_configs("multi",        { description = "Enable multi-thread support.",  default = true, type = "boolean"})
-    for _, libname in ipairs(libnames) do
-        add_configs(libname,    { description = "Enable " .. libname .. " library.", default = (libname == "filesystem"), type = "boolean"})
-    end
-    add_configs("zstd", {description = "enable zstd for iostreams", default = false, type = "boolean"})
-    add_configs("lzma", {description = "enable lzma for iostreams", default = false, type = "boolean"})
-    add_configs("zlib", {description = "enable zlib for iostreams", default = false, type = "boolean"})
-    add_configs("bzip2", {description = "enable bzip2 for iostreams", default = false, type = "boolean"})
-
-    on_load(function (package)
-
-        local function get_linkname(package, libname)
-            local linkname
-            if package:is_plat("windows") then
-                linkname = (package:config("shared") and "boost_" or "libboost_") .. libname
-            else
-                linkname = "boost_" .. libname
-            end
-            if libname == "python" or libname == "numpy" then
-                linkname = linkname .. package:config("pyver"):gsub("%p+", "")
-            end
-            if package:config("multi") then
-                linkname = linkname .. "-mt"
-            end
-            if package:is_plat("windows") then
-                if package:config("shared") then
-                    if package:debug() then
-                        linkname = linkname .. "-gd"
-                    end
-                elseif package:config("asan") or package:has_runtime("MTd") then
-                    linkname = linkname .. "-sgd"
-                elseif package:has_runtime("MT") then
-                    linkname = linkname .. "-s"
-                elseif package:config("asan") or package:has_runtime("MDd") then
-                    linkname = linkname .. "-gd"
-                end
-            else
-                if package:debug() then
-                    linkname = linkname .. "-d"
-                end
-            end
-            return linkname
-        end
-
-        -- we need the fixed link order
-        local headeronly = not package:config("all")
-        local sublibs = {log = {"log_setup", "log"},
-                        python = {"python", "numpy"},
-                        stacktrace = {"stacktrace_backtrace", "stacktrace_basic"}}
-        for _, libname in ipairs(libnames) do
-            if package:config(libname) then
-                headeronly = false
-            end
-            local libs = sublibs[libname]
-            if libs then
-                for _, lib in ipairs(libs) do
-                    package:add("links", get_linkname(package, lib))
-                end
-            else
-                package:add("links", get_linkname(package, libname))
-            end
-        end
-        if headeronly then
-            package:set("kind", "library", {headeronly = true})
-        end
-        -- disable auto-link all libs
-        if package:is_plat("windows") then
-            package:add("defines", "BOOST_ALL_NO_LIB")
-        end
-
-        if package:config("python") then
-            if not package:config("shared") then
-                package:add("defines", "BOOST_PYTHON_STATIC_LIB")
-            end
-            package:add("deps", "python " .. package:config("pyver") .. ".x", {configs = {headeronly = true}})
-        end
-        if package:config("zstd") then
-            package:add("deps", "zstd")
-        end
-        if package:config("lzma") then
-            package:add("deps", "xz")
-        end
-        if package:config("zlib") then
-            package:add("deps", "zlib")
-        end
-        if package:config("bzip2") then
-            package:add("deps", "bzip2")
-        end
-
-        if package:is_plat("windows") and package:version():le("1.85.0") then
-            local vs_toolset = package:toolchain("msvc"):config("vs_toolset")
-            if vs_toolset then
-                local vs_toolset_ver = import("core.base.semver").new(vs_toolset)
-                local minor = vs_toolset_ver:minor()
-                if minor and minor >= 40 then
-                    package:add("patches", "<=1.85.0", "patches/1.85.0/fix-v144.patch", "1ba99cb2e2f03a4ba489a32596c62e1310b6c73ba4d19afa8796bcf180c84422")
-                end
-            end
-        end
-    end)
-
-    on_install("macosx", "linux", "windows", "bsd", "mingw", "cross", function (package)
-        import("core.base.option")
-
-        local function get_compiler(package, toolchain)
-            local cxx = package:build_getenv("cxx")
-            if package:is_plat("macosx") then
-                -- we uses ld/clang++ for link stdc++ for shared libraries
-                -- and we need `xcrun -sdk macosx clang++` to make b2 to get `-isysroot` automatically
-                local cc = package:build_getenv("ld")
-                if cc and cc:find("clang", 1, true) and cc:find("Xcode", 1, true) then
-                    cc = "xcrun -sdk macosx clang++"
-                end
-                return format("using darwin : : %s ;", cc)
-            elseif package:is_plat("windows") then
-                local vs_toolset = toolchain:config("vs_toolset")
-                local msvc_ver = ""
-                local win_toolset = "msvc"
-                if toolchain:name() == "clang-cl" then
-                    win_toolset = "clang-win"
-                    cxx = cxx:gsub("(clang%-cl)$", "%1.exe", 1)
-                    msvc_ver = ""
-                elseif vs_toolset then
-                    local i = vs_toolset:find("%.")
-                    msvc_ver = i and vs_toolset:sub(1, i + 1)
-                end
-
-                -- Specifying a version will disable b2 from forcing tools
-                -- from the latest installed msvc version.
-                return format("using %s : %s : \"%s\" ;", win_toolset, msvc_ver, cxx:gsub("\\", "\\\\"))
-            else
-                cxx = cxx:gsub("gcc$", "g++")
-                cxx = cxx:gsub("gcc%-", "g++-")
-                cxx = cxx:gsub("clang$", "clang++")
-                cxx = cxx:gsub("clang%-", "clang++-")
-                if cxx and cxx:find("clang", 1, true) then
-                    return format("using clang : : \"%s\" ;", cxx:gsub("\\", "/"))
-                else
-                    return format("using gcc : : \"%s\" ;", cxx:gsub("\\", "/"))
+    if on_check then
+        on_check(function (package)
+            if not package:is_plat("macosx", "linux", "windows", "bsd", "mingw", "cross") then
+                if not package:config("cmake") then
+                    raise("package(boost/b2) unsupported current platform.")
                 end
             end
-        end
 
-        -- get host toolchain
-        import("core.tool.toolchain")
-        local host_toolchain
-        if package:is_plat("windows") then
-            host_toolchain = toolchain.load("msvc", {plat = "windows", arch = os.arch()})
-            if not host_toolchain:check() then
-                host_toolchain = toolchain.load("clang-cl", {plat = "windows", arch = os.arch()})
+            local version = package:version()
+            if package:config("cmake") and version:lt("1.86") then
+                raise("package(boost/cmake) only support >= 1.86.0 version")
             end
-            assert(host_toolchain:check(), "host msvc or clang-cl not found!")
-        end
-
-        -- force boost to compile with the desired compiler
-        local file = io.open("user-config.jam", "w")
-        if file then
-            file:write(get_compiler(package, host_toolchain))
-            file:close()
-        end
-
-        local bootstrap_argv =
-        {
-            "--prefix=" .. package:installdir(),
-            "--libdir=" .. package:installdir("lib"),
-            "--without-icu"
-        }
+        end)
+    end
 
-        if package:has_tool("cxx", "clang", "clangxx") then
-            table.insert(bootstrap_argv, "--with-toolset=clang")
-        end
+    on_load(function (package)
+        local version = package:version()
+        if package:config("cmake") and version:ge("1.86") then
+            wprint("If cmake build failure, set package config cmake = false fallback to b2 for the build")
 
-        if package:is_plat("windows") then
-            -- for bootstrap.bat, all other arguments are useless
-            bootstrap_argv = { "msvc" }
-            os.vrunv("bootstrap.bat", bootstrap_argv, {envs = host_toolchain:runenvs()})
-        elseif package:is_plat("mingw") and is_host("windows") then
-            bootstrap_argv = { "gcc" }
-            os.vrunv("bootstrap.bat", bootstrap_argv)
-            -- todo looking for better solution to fix the confict between user-config.jam and project-config.jam
-            io.replace("project-config.jam", "using[^\n]+", "")
+            package:add("deps", "cmake")
+            import("cmake.load")(package)
         else
-            os.vrunv("./bootstrap.sh", bootstrap_argv)
-        end
-
-        -- get build toolchain
-        local build_toolchain
-        local build_toolset
-        local runenvs
-        if package:is_plat("windows") then
-            if package:has_tool("cxx", "clang_cl") then
-                build_toolset = "clang-win"
-                build_toolchain = package:toolchain("clang-cl")
-            elseif package:has_tool("cxx", "clang") then
-                build_toolset = "clang-win"
-                build_toolchain = package:toolchain("clang") or package:toolchain("llvm")
-            elseif package:has_tool("cxx", "cl") then
-                build_toolset = "msvc"
-                build_toolchain = package:toolchain("msvc")
-            end
-            if build_toolchain then
-                runenvs = build_toolchain:runenvs()
-            end
-        end
-
-        local function config_deppath(file, depname, rule)
-            local dep = package:dep(depname)
-            local info = dep:fetch({external = false})
-            if info then
-                local includedirs = table.wrap(info.sysincludedirs or info.includedirs)
-                for i, dir in ipairs(includedirs) do
-                    includedirs[i] = path.unix(dir)
-                end
-                local linkdirs = table.wrap(info.linkdirs)
-                for i, dir in ipairs(linkdirs) do
-                    linkdirs[i] = path.unix(dir)
-                end
-                local links = table.wrap(info.links)
-                local usingstr = format("\nusing %s : %s : <include>%s <search>%s <name>%s ;",
-                    rule, dep:version(),
-                    table.concat(includedirs, ";"),
-                    table.concat(linkdirs, ";"),
-                    table.concat(links, ";"))
-                file:write(usingstr)
-            end
-        end
-        local file = io.open("user-config.jam", "w")
-        if file then
-            file:write(get_compiler(package, build_toolchain))
-            if package:config("lzma") then
-                config_deppath(file, "xz", "lzma")
-            end
-            if package:config("zstd") then
-                config_deppath(file, "zstd", "zstd")
-            end
-            if package:config("zlib") then
-                config_deppath(file, "zlib", "zlib")
-            end
-            if package:config("bzip2") then
-                config_deppath(file, "bzip2", "bzip2")
+            if package:is_plat("linux") then
+                package:add("deps", "bzip2", "zlib")
             end
-            file:close()
-        end
-        os.vrun("./b2 headers")
-
-        local njobs = option.get("jobs") or tostring(os.default_njob())
-        local argv =
-        {
-            "--prefix=" .. package:installdir(),
-            "--libdir=" .. package:installdir("lib"),
-            "-d2",
-            "-j" .. njobs,
-            "--hash",
-            "-q", -- quit on first error
-            "--layout=tagged-1.66", -- prevent -x64 suffix in case cmake can't find it
-            "--user-config=user-config.jam",
-            "install",
-            "threading=" .. (package:config("multi") and "multi" or "single"),
-            "debug-symbols=" .. (package:debug() and "on" or "off"),
-            "link=" .. (package:config("shared") and "shared" or "static"),
-            "variant=" .. (package:is_debug() and "debug" or "release"),
-            "runtime-debugging=" .. (package:is_debug() and "on" or "off")
-        }
-
-        local cxxflags = {}
-        if package:config("lzma") then
-            if package:is_plat("windows") and not package:dep("xz"):config("shared") then
-                table.insert(cxxflags, "-DLZMA_API_STATIC")
-            end
-        else
-            table.insert(argv, "-sNO_LZMA=1")
-        end
-        if not package:config("zstd") then
-            table.insert(argv, "-sNO_ZSTD=1")
-        end
-        if not package:config("zlib") then
-            table.insert(argv, "-sNO_ZLIB=1")
-        end
-        if not package:config("bzip2") then
-            table.insert(argv, "-sNO_BZIP2=1")
-        end
-
-        if package:config("lto") then
-            table.insert(argv, "lto=on")
-        end
-        if package:is_arch("aarch64", "arm+.*") then
-            table.insert(argv, "architecture=arm")
-        end
-        if package:is_arch(".+64.*") then
-            table.insert(argv, "address-model=64")
-        else
-            table.insert(argv, "address-model=32")
+            import("b2.load")(package)
         end
+    end)
 
-        local linkflags = {}
-        table.join2(cxxflags, table.wrap(package:config("cxflags")))
-        table.join2(cxxflags, table.wrap(package:config("cxxflags")))
-        if package:is_plat("windows") then
-            if package:config("shared") then
-                table.insert(argv, "runtime-link=shared")
-            elseif package:has_runtime("MT", "MTd") then
-                table.insert(argv, "runtime-link=static")
-            else
-                table.insert(argv, "runtime-link=shared")
-            end
-            table.insert(argv, "toolset=" .. build_toolset)
-            table.insert(cxxflags, "-std:c++14")
-        elseif package:is_plat("mingw") then
-            table.insert(argv, "toolset=gcc")
-        elseif package:is_plat("macosx") then
-            table.insert(argv, "toolset=darwin")
-
-            -- fix macosx arm64 build issue https://github.com/microsoft/vcpkg/pull/18529
-            table.insert(cxxflags, "-std=c++14")
-            table.insert(cxxflags, "-arch")
-            table.insert(cxxflags, package:arch())
-            local xcode = package:toolchain("xcode") or import("core.tool.toolchain").load("xcode", {plat = package:plat(), arch = package:arch()})
-            if xcode:check() then
-                local xcode_dir = xcode:config("xcode")
-                local xcode_sdkver = xcode:config("xcode_sdkver")
-                local target_minver = xcode:config("target_minver")
-                if xcode_dir and xcode_sdkver then
-                    local xcode_sdkdir = xcode_dir .. "/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX" .. xcode_sdkver .. ".sdk"
-                    table.insert(cxxflags, "-isysroot")
-                    table.insert(cxxflags, xcode_sdkdir)
-                end
-                if target_minver then
-                    table.insert(cxxflags, "-mmacosx-version-min=" .. target_minver)
-                end
-            end
+    on_install(function (package)
+        local version = package:version()
+        if package:config("cmake") and version:ge("1.86") then
+            assert(os.isfile("CMakeLists.txt"), "Currently the source archive only has the b2 build system, you need to download the cmake archive and put it in `xmake g --pkg_searchdirs=` to avoid xrepo using a non-cmake archive url.")
+            import("cmake.install")(package)
         else
-            table.insert(cxxflags, "-std=c++14")
-            if package:config("pic") ~= false then
-                table.insert(cxxflags, "-fPIC")
-            end
-        end
-        if package.has_runtime and package:has_runtime("c++_shared", "c++_static") then
-            table.insert(cxxflags, "-stdlib=libc++")
-            table.insert(linkflags, "-stdlib=libc++")
-            if package:has_runtime("c++_static") then
-                table.insert(linkflags, "-static-libstdc++")
-            end
-        end
-        if package:config("asan") then
-            table.insert(cxxflags, "-fsanitize=address")
-            table.insert(linkflags, "-fsanitize=address")
-        end
-        if cxxflags then
-            table.insert(argv, "cxxflags=" .. table.concat(cxxflags, " "))
-        end
-        if linkflags then
-            table.insert(argv, "linkflags=" .. table.concat(linkflags, " "))
-        end
-        for _, libname in ipairs(libnames) do
-            if package:config("all") or package:config(libname) then
-                table.insert(argv, "--with-" .. libname)
-            end
-        end
-
-        if package:is_plat("linux") then
-            table.insert(argv, "pch=off")
-        end
-        local ok = os.execv("./b2", argv, {envs = runenvs, try = true, stdout = "boost-log.txt"})
-        if ok ~= 0 then
-            raise("boost build failed, please check log in " .. path.join(os.curdir(), "boost-log.txt"))
+            import("b2.install")(package)
         end
     end)
 
     on_test(function (package)
-        assert(package:check_cxxsnippets({test = [[
-            #include <boost/algorithm/string.hpp>
-            #include <string>
-            #include <vector>
-            static void test() {
-                std::string str("a,b");
-                std::vector<std::string> vec;
-                boost::algorithm::split(vec, str, boost::algorithm::is_any_of(","));
-            }
-        ]]}, {configs = {languages = "c++14"}}))
-
-        assert(package:check_cxxsnippets({test = [[
-            #include <boost/unordered_map.hpp>
-            static void test() {
-                boost::unordered_map<std::string, int> map;
-                map["2"] = 2;
-            }
-        ]]}, {configs = {languages = "c++14"}}))
-
-        if package:config("date_time") then
-            assert(package:check_cxxsnippets({test = [[
-                #include <boost/date_time/gregorian/gregorian.hpp>
-                static void test() {
-                    boost::gregorian::date d(2010, 1, 30);
-                }
-            ]]}, {configs = {languages = "c++14"}}))
-        end
-
-        if package:config("filesystem") then
-            assert(package:check_cxxsnippets({test = [[
-                #include <boost/filesystem.hpp>
-                #include <iostream>
-                static void test() {
-                    boost::filesystem::path path("/path/to/directory");
-                    if (boost::filesystem::exists(path)) {
-                        std::cout << "Directory exists" << std::endl;
-                    } else {
-                        std::cout << "Directory does not exist" << std::endl;
-                    }
-                }
-            ]]}, {configs = {languages = "c++14"}}))
-        end
-
-        if package:config("iostreams") then
-            if package:config("zstd") then
-                assert(package:check_cxxsnippets({test = [[
-                    #include <boost/iostreams/filter/zstd.hpp>
-                    #include <boost/iostreams/filtering_stream.hpp>
-                    static void test() {
-                        boost::iostreams::filtering_ostream out;
-                        out.push(boost::iostreams::zstd_compressor());
-                    }
-                ]]}, {configs = {languages = "c++14"}}))
-            end
-            if package:config("lzma") then
-                assert(package:check_cxxsnippets({test = [[
-                    #include <boost/iostreams/filter/lzma.hpp>
-                    #include <boost/iostreams/filtering_stream.hpp>
-                    static void test() {
-                        boost::iostreams::filtering_ostream out;
-                        out.push(boost::iostreams::lzma_compressor());
-                    }
-                ]]}, {configs = {languages = "c++14"}}))
-            end
-        end
+        import("test")(package)
     end)

+ 30 - 0
packages/b/boostdep/port/xmake.lua

@@ -0,0 +1,30 @@
+-- Boost Root build
+
+add_rules("mode.debug", "mode.release")
+
+set_languages("c++17")
+
+target("filesystem")
+    set_kind("static")
+    add_files("libs/filesystem/src/*.cpp|windows_file_codecvt.cpp")
+
+    add_defines("BOOST_FILESYSTEM_NO_CXX20_ATOMIC_REF")
+    add_defines("BOOST_FILESYSTEM_STATIC_LINK=1", {public = true})
+
+    for _, dir in ipairs(os.dirs("libs/*")) do
+        add_includedirs(path.join(dir, "include"), {public = true})
+    end
+
+    if is_plat("windows", "mingw", "msys2") then
+        add_files("libs/filesystem/src/*.cpp")
+        add_defines("BOOST_USE_WINDOWS_H", "WIN32_LEAN_AND_MEAN", "NOMINMAX")
+        add_syslinks("bcrypt")
+        if is_plat("windows") then
+            add_defines("BOOST_ALL_NO_LIB", {public = true})
+        end
+    end
+
+target("boostdep")
+    set_kind("binary")
+    add_files("tools/boostdep/src/*.cpp")
+    add_deps("filesystem")

+ 31 - 0
packages/b/boostdep/xmake.lua

@@ -0,0 +1,31 @@
+package("boostdep")
+    set_kind("binary")
+    set_homepage("https://boost.org/tools/boostdep")
+    set_description("A tool to create Boost module dependency reports")
+    set_license("BSL-1.0")
+
+    add_urls("https://github.com/boostorg/boostdep.git")
+    add_versions("2024.10.07", "289f2a16286e62348676f2abb75c0bd9968f156b")
+
+    add_deps("boost", {configs = {filesystem = true}})
+
+    on_install(function (package)
+        io.writefile("xmake.lua", [[
+            add_rules("mode.debug", "mode.release")
+            add_requires("boost", {configs = {filesystem = true}})
+            add_packages("boost")
+            set_languages("c++17")
+            target("boostdep")
+                set_kind("binary")
+                add_files("src/*.cpp")
+        ]])
+        import("package.tools.xmake").install(package)
+    end)
+
+    on_test(function (package)
+        local boostdep = package:installdir("bin/boostdep")
+        if is_host("windows") then
+            boostdep = boostdep .. ".exe"
+        end
+        assert(os.isexec(boostdep), "boostdep not found!")
+    end)