From f8d32f6219aee0b8ff7178c8395e26b16819e4c0 Mon Sep 17 00:00:00 2001
From: Maxwell Geng <33229754+MaxwellGengYF@users.noreply.github.com>
Date: Wed, 29 May 2024 13:50:14 +0800
Subject: [PATCH] xmake build support

---
 .gitignore           |   1 +
 xmake.lua            | 100 ++++++++++++++++++++++
 xmake/lib.lua        | 199 +++++++++++++++++++++++++++++++++++++++++++
 xmake/xmake_func.lua | 161 ++++++++++++++++++++++++++++++++++
 4 files changed, 461 insertions(+)
 create mode 100644 xmake.lua
 create mode 100644 xmake/lib.lua
 create mode 100644 xmake/xmake_func.lua

diff --git a/.gitignore b/.gitignore
index 8de6d91d..309c967b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,6 +5,7 @@
 /coverage/
 /.vs/
 /.vscode/
+/.xmake/
 /fuzz/luau.pb.*
 /crash-*
 /default.prof*
diff --git a/xmake.lua b/xmake.lua
new file mode 100644
index 00000000..2a6e1217
--- /dev/null
+++ b/xmake.lua
@@ -0,0 +1,100 @@
+set_xmakever('2.9.2')
+add_rules('mode.release', 'mode.debug')
+set_policy('build.ccache', not is_plat('windows'))
+includes('xmake/xmake_func.lua')
+
+target('luau_base')
+set_kind('phony')
+add_defines('LUA_USE_LONGJMP', {public = true})
+add_includedirs('Common/include', {public = true})
+
+target('luau_vm')
+_config_project({
+    project_kind = 'static'
+})
+add_files('VM/src/*.cpp')
+add_includedirs('VM/include', 'VM/src', {
+    public = true
+})
+add_deps('luau_base')
+target_end()
+
+
+target('luau_codegen')
+_config_project({
+    project_kind = 'static'
+})
+add_files('Codegen/src/*.cpp')
+add_includedirs('Codegen/include', {
+    public = true
+})
+add_deps('luau_vm')
+target_end()
+
+
+target('luau_ast')
+_config_project({
+    project_kind = 'static',
+    enable_exception = true
+})
+add_files('Ast/src/*.cpp')
+add_includedirs('Ast/include', {
+    public = true
+})
+add_deps('luau_base')
+target_end()
+
+target('luau_compiler')
+_config_project({
+    project_kind = 'static',
+    enable_exception = true
+})
+add_files('Compiler/src/*.cpp')
+add_includedirs('Compiler/include', {
+    public = true
+})
+add_deps('luau_ast')
+target_end()
+
+
+target('luau_config')
+_config_project({
+    project_kind = 'static',
+})
+add_files('Config/src/*.cpp')
+add_includedirs('Config/include', {
+    public = true
+})
+add_deps('luau_ast')
+target_end()
+
+
+target('luau_analysis')
+_config_project({
+    project_kind = 'static',
+    enable_exception = true
+})
+add_files('Analysis/src/*.cpp')
+add_includedirs('Analysis/include', {
+    public = true
+})
+add_deps('luau_config')
+target_end()
+
+
+target('isocline')
+_config_project({project_kind = 'static',})
+add_includedirs('extern/isocline/include', {public = true})
+add_files('extern/isocline/src/isocline.c')
+target_end()
+
+
+target('luau')
+_config_project({
+    project_kind = 'binary',
+    enable_exception = true
+})
+add_files('CLI/ReplEntry.cpp', 'CLI/Repl.cpp', 'CLI/Flags.cpp', 'CLI/FileUtils.cpp', 'CLI/Profiler.cpp', 'CLI/Coverage.cpp', 'CLI/Require.cpp')
+add_deps('luau_compiler', 'luau_analysis', 'luau_vm', 'luau_codegen', 'isocline')
+target_end()
+
diff --git a/xmake/lib.lua b/xmake/lib.lua
new file mode 100644
index 00000000..1b2df54a
--- /dev/null
+++ b/xmake/lib.lua
@@ -0,0 +1,199 @@
+local function _mkdirs(p)
+    if os.exists(p) then
+        return
+    end
+    try {function()
+        local dir = path.directory(p)
+        if dir then
+            _mkdirs(dir)
+        end
+        os.mkdir(p)
+    end, catch {function()
+    end}}
+end
+function mkdirs(p)
+    _mkdirs(path.translate(p))
+end
+
+function string_split(str, chr)
+    return str:split(chr, {plain = true})
+end
+function string_replace(str, from, to)
+    local s, _ = str:gsub(from, to)
+    return s
+end
+
+function string_contains(str, sub_str)
+    return str:match(sub_str) ~= nil
+end
+local libc = import("core/base/libc")
+local bytes = import("core/base/bytes")
+local _string_builder = {}
+function _string_builder:to_string()
+    return libc.strndup(self._ptr + 1, self._size)
+end
+local function _add_capacity(self, s)
+    local size = s + self._size
+    local capa = self._capacity
+    if capa >= size then
+        return
+    end
+    while capa < size do
+        capa = capa * 2
+    end
+    local old_ptr = self._ptr + 1
+    local new_ptr = libc.malloc(capa)
+    libc.memcpy(new_ptr, old_ptr, self._size)
+    libc.free(old_ptr)
+    self._ptr = new_ptr - 1
+    self._capacity = capa
+end
+function _string_builder:reserve(s)
+    local capa = self._capacity
+    if capa >= s then
+        return
+    end
+    local old_ptr = self._ptr + 1
+    local new_ptr = libc.malloc(s)
+    libc.memcpy(new_ptr, old_ptr, self._size)
+    libc.free(old_ptr)
+    self._ptr = new_ptr - 1
+    self._capacity = s
+end
+function _string_builder:equal(str)
+    local str_ptr
+    local str_size
+    if type(str) == "string" then
+        str_ptr = libc.dataptr(str)
+        str_size = #str
+    else
+        str_ptr = str:caddr()
+        str_size = str:size()
+    end
+    if str_size ~= self.size() then
+        return false
+    end
+    local ptr = self._ptr + self._size + 1
+    return libc.memcmp(ptr, str_ptr, str_size) == 0
+end
+function _string_builder:add(str)
+    local str_ptr
+    local str_size
+    if type(str) == "string" then
+        str_ptr = libc.dataptr(str)
+        str_size = #str
+    else
+        str_ptr = str:caddr()
+        str_size = str:size()
+    end
+    if str_size == 0 then
+        return
+    end
+    _add_capacity(self, str_size)
+    local ptr = self._ptr + self._size + 1
+    libc.memcpy(ptr, str_ptr, str_size)
+    self._size = self._size + str_size
+    return self
+end
+function _string_builder:subview(offset, size)
+    local sf = self
+    return {
+        _size = math.min(sf._size - offset + 1, size),
+        _ptr = sf._ptr + offset,
+        size = function(self)
+            return self._size
+        end,
+        caddr = function(self)
+            return self._ptr
+        end
+    }
+end
+function _string_builder:add_char(c)
+    _add_capacity(self, 1)
+    self._size = self._size + 1
+    libc.setbyte(self._ptr, self._size, c)
+    return self
+end
+function _string_builder:dispose()
+    if self._ptr ~= -1 then
+        libc.free(self._ptr + 1)
+        self._ptr = -1
+    end
+end
+function _string_builder:write_to(path)
+    local f = io.open(path, "wb")
+    f:write(self)
+    f:close()
+end
+function _string_builder:get(i)
+    return libc.byteof(self._ptr, i)
+end
+function _string_builder:set(i, v)
+    return libc.setbyte(self._ptr, i, v)
+end
+function _string_builder:erase(i)
+    self._size = math.max(self._size - i, 1)
+end
+function _string_builder:size()
+    return self._size
+end
+function _string_builder:capacity()
+    return self._capacity
+end
+function _string_builder:caddr()
+    return self._ptr + 1
+end
+function _string_builder:cdata()
+    return self._ptr + 1
+end
+function _string_builder:clear()
+    self._size = 0
+end
+function StringBuilder(str)
+    local inst = table.inherit(_string_builder)
+    if str then
+        local str_ptr
+        local str_size
+        if type(str) == "string" then
+            str_ptr = libc.dataptr(str)
+            str_size = #str
+        else
+            str_ptr = str:caddr()
+            str_size = str:size()
+        end
+        local capa = math.max(32, str_size)
+        local addr = libc.malloc(capa)
+        inst._size = str_size
+        inst._capacity = capa
+        inst._ptr = addr - 1
+        libc.memcpy(addr, str_ptr, str_size)
+    else
+        inst._size = 0
+        inst._capacity = 32
+        inst._ptr = libc.malloc(32) - 1
+    end
+    return inst
+end
+function char(str)
+    return libc.byteof(libc.dataptr(str), 0)
+end
+function to_byte_array(input, out)
+    if input:size() <= 0 then
+        return 0
+    end
+    local cut = char(',')
+    local str_ptr
+    local str_size
+    if type(input) == "string" then
+        str_ptr = libc.dataptr(input)
+        str_size = #input
+    else
+        str_ptr = input:caddr()
+        str_size = input:size()
+    end
+    for i = 0, (str_size - 1) do
+        out:add(tostring(libc.byteof(str_ptr, i))):add_char(cut)
+    end
+    out:erase(1)
+    return str_size
+end
diff --git a/xmake/xmake_func.lua b/xmake/xmake_func.lua
new file mode 100644
index 00000000..d116a129
--- /dev/null
+++ b/xmake/xmake_func.lua
@@ -0,0 +1,161 @@
+rule("luau_basic_settings")
+on_config(function(target)
+    local _, cc = target:tool("cxx")
+    if is_plat("linux") then
+        -- Linux should use -stdlib=libc++
+        -- https://github.com/LuisaGroup/LuisaCompute/issues/58
+        if (cc == "clang" or cc == "clangxx") then
+            target:add("cxflags", "-stdlib=libc++", {
+                force = true
+            })
+            target:add("syslinks", "c++")
+        end
+    end
+    -- disable LTO
+    -- if cc == "cl" then
+    --     target:add("cxflags", "-GL")
+    -- elseif cc == "clang" or cc == "clangxx" then
+    --     target:add("cxflags", "-flto=thin")
+    -- elseif cc == "gcc" or cc == "gxx" then
+    --     target:add("cxflags", "-flto")
+    -- end
+    -- local _, ld = target:tool("ld")
+    -- if ld == "link" then
+    --     target:add("ldflags", "-LTCG")
+    --     target:add("shflags", "-LTCG")
+    -- elseif ld == "clang" or ld == "clangxx" then
+    --     target:add("ldflags", "-flto=thin")
+    --     target:add("shflags", "-flto=thin")
+    -- elseif ld == "gcc" or ld == "gxx" then
+    --     target:add("ldflags", "-flto")
+    --     target:add("shflags", "-flto")
+    -- end
+end)
+on_load(function(target)
+    local _get_or = function(name, default_value)
+        local v = target:extraconf("rules", "luau_basic_settings", name)
+        if v == nil then
+            return default_value
+        end
+        return v
+    end
+    local project_kind = _get_or("project_kind", nil)
+    if project_kind then
+        target:set("kind", project_kind)
+    end
+    if not is_plat("windows") then
+        if project_kind == "static" or project_kind == "object" then
+            target:add("cxflags", "-fPIC", {
+                tools = {"clang", "gcc"}
+            })
+        end
+    end
+    -- fma support
+    if is_arch("x64", "x86_64") then
+        target:add("cxflags", "-mfma", {
+            tools = {"clang", "gcc"}
+        })
+    end
+    local c_standard = target:values("c_standard")
+    local cxx_standard = target:values("cxx_standard")
+    if type(c_standard) == "string" and type(cxx_standard) == "string" then
+        target:set("languages", c_standard, cxx_standard, {
+            public = true
+        })
+    else
+        target:set("languages", "clatest", "cxx20", {
+            public = true
+        })
+    end
+
+    local enable_exception = _get_or("enable_exception", nil)
+    if enable_exception then
+        target:set("exceptions", "cxx")
+    else
+        target:set("exceptions", "no-cxx")
+    end
+
+    if is_mode("debug") then
+        target:set("runtimes", _get_or("runtime", "MDd"), {
+            public = true
+        })
+        target:set("optimize", "none")
+        target:set("warnings", "none")
+        target:add("cxflags", "/GS", "/Gd", {
+            tools = {"clang_cl", "cl"}
+        })
+    elseif is_mode("releasedbg") then
+        target:set("runtimes", _get_or("runtime", "MD"), {
+            public = true
+        })
+        target:set("optimize", "none")
+        target:set("warnings", "none")
+        target:add("cxflags", "/GS-", "/Gd", {
+            tools = {"clang_cl", "cl"}
+        })
+    else
+        target:set("runtimes", _get_or("runtime", "MD"), {
+            public = true
+        })
+        target:set("optimize", "aggressive")
+        target:set("warnings", "none")
+        target:add("cxflags", "/GS-", "/Gd", {
+            tools = {"clang_cl", "cl"}
+        })
+    end
+    target:set("fpmodels", "fast")
+    target:add("cxflags", "/Zc:preprocessor", {
+        tools = "cl",
+        public = true
+    });
+    if _get_or("use_simd", true) then
+        if is_arch("arm64") then
+            target:add("vectorexts", "neon")
+        else
+            target:add("vectorexts", "avx", "avx2")
+        end
+    end
+    if _get_or("no_rtti", true) then
+        target:add("cxflags", "/GR-", {
+            tools = {"clang_cl", "cl"},
+            public = true
+        })
+        target:add("cxflags", "-fno-rtti", "-fno-rtti-data", {
+            tools = {"clang"},
+            public = true
+        })
+        target:add("cxflags", "-fno-rtti", {
+            tools = {"gcc"},
+            public = true
+        })
+    end
+end)
+rule_end()
+
+
+-- In-case of submod, when there is override rules, do not overload
+if _config_rules == nil then
+    _config_rules = {"luau_basic_settings"}
+end
+if _disable_unity_build == nil then
+    local unity_build = get_config("enable_unity_build")
+    if unity_build ~= nil then
+        _disable_unity_build = not unity_build
+    end
+end
+if not _config_project then
+    function _config_project(config)
+        local batch_size = config["batch_size"]
+        if type(batch_size) == "number" and batch_size > 1 and (not _disable_unity_build) then
+            add_rules("c.unity_build", {
+                batchsize = batch_size
+            })
+            add_rules("c++.unity_build", {
+                batchsize = batch_size
+            })
+        end
+        if type(_config_rules) == "table" then
+            add_rules(_config_rules, config)
+        end
+    end
+end