xmake.lua 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. package("mquickjs")
  2. set_homepage("https://github.com/bellard/mquickjs")
  3. set_description("Public repository of the Micro QuickJS Javascript Engine")
  4. add_urls("https://github.com/bellard/mquickjs.git")
  5. add_versions("2025.12.22", "17ce6fe54c1ea4f500f26636bd22058fce2ce61a")
  6. if is_plat("linux", "macosx", "bsd") then
  7. add_syslinks("m")
  8. end
  9. on_load(function (package)
  10. package:addenv("PATH", "bin")
  11. end)
  12. on_install(function (package)
  13. io.replace("mquickjs.h", "#include <inttypes.h>", "#include <inttypes.h>\n#include <stddef.h>", {plain = true})
  14. io.replace("libm.c", "#define NDEBUG", "", {plain = true})
  15. os.cp("cutils.h", "cutils_host.h")
  16. io.replace("mqjs_stdlib.c", "cutils.h", "cutils_host.h", {plain = true})
  17. io.replace("mquickjs_build.c", "cutils.h", "cutils_host.h", {plain = true})
  18. -- Add MSVC compat definitions
  19. local msvc_compat = [[
  20. #ifdef _MSC_VER
  21. #include <intrin.h>
  22. #include <sys/timeb.h>
  23. #include <time.h>
  24. #define likely(x) (x)
  25. #define unlikely(x) (x)
  26. #define force_inline __forceinline
  27. #define no_inline __declspec(noinline)
  28. #define __maybe_unused
  29. #define __attribute__(x)
  30. /* Type-generic macros for overflow checking */
  31. #define __builtin_add_overflow(a, b, res) \
  32. ((sizeof(a) == 8) ? _add_overflow_u64((uint64_t)(a), (uint64_t)(b), (uint64_t*)(res)) : \
  33. _add_overflow_u32((uint32_t)(a), (uint32_t)(b), (uint32_t*)(res)))
  34. #define __builtin_sub_overflow(a, b, res) \
  35. ((sizeof(a) == 8) ? _sub_overflow_u64((uint64_t)(a), (uint64_t)(b), (uint64_t*)(res)) : \
  36. _sub_overflow_u32((uint32_t)(a), (uint32_t)(b), (uint32_t*)(res)))
  37. static inline int _add_overflow_u64(uint64_t a, uint64_t b, uint64_t *res) {
  38. *res = a + b;
  39. return (*res < a);
  40. }
  41. static inline int _add_overflow_u32(uint32_t a, uint32_t b, uint32_t *res) {
  42. *res = a + b;
  43. return (*res < a);
  44. }
  45. static inline int _sub_overflow_u64(uint64_t a, uint64_t b, uint64_t *res) {
  46. *res = a - b;
  47. return (a < b);
  48. }
  49. static inline int _sub_overflow_u32(uint32_t a, uint32_t b, uint32_t *res) {
  50. *res = a - b;
  51. return (a < b);
  52. }
  53. static inline int __builtin_clzll(uint64_t x) {
  54. #ifdef _WIN64
  55. unsigned long idx;
  56. if (_BitScanReverse64(&idx, x)) return 63 - idx;
  57. return 64;
  58. #else
  59. unsigned long idx;
  60. if (_BitScanReverse(&idx, (uint32_t)(x >> 32))) return 31 - idx;
  61. if (_BitScanReverse(&idx, (uint32_t)x)) return 63 - idx;
  62. return 64;
  63. #endif
  64. }
  65. static inline int __builtin_clz(unsigned int x) {
  66. unsigned long idx;
  67. if (_BitScanReverse(&idx, x)) return 31 - idx;
  68. return 32;
  69. }
  70. static inline int __builtin_ctzll(uint64_t x) {
  71. #ifdef _WIN64
  72. unsigned long idx;
  73. if (_BitScanForward64(&idx, x)) return idx;
  74. return 64;
  75. #else
  76. unsigned long idx;
  77. if (_BitScanForward(&idx, (uint32_t)x)) return idx;
  78. if (_BitScanForward(&idx, (uint32_t)(x >> 32))) return 32 + idx;
  79. return 64;
  80. #endif
  81. }
  82. static inline int __builtin_ctz(unsigned int x) {
  83. unsigned long idx;
  84. if (_BitScanForward(&idx, x)) return idx;
  85. return 32;
  86. }
  87. #if !defined(_WINSOCK2API_) && !defined(_WINSOCKAPI_)
  88. struct timeval {
  89. long tv_sec;
  90. long tv_usec;
  91. };
  92. #endif
  93. static int gettimeofday(struct timeval *tp, void *tzp) {
  94. struct __timeb64 t;
  95. _ftime64(&t);
  96. tp->tv_sec = (long)t.time;
  97. tp->tv_usec = t.millitm * 1000;
  98. return 0;
  99. }
  100. #endif
  101. ]]
  102. if is_host("windows") then
  103. -- Fix packed structs
  104. io.replace("cutils_host.h", "struct __attribute__%(%(packed%)%) (packed_u%d+) {\n%s+(uint%d+_t) v;\n};",
  105. "#ifdef _MSC_VER\n#pragma pack(push, 1)\nstruct %1 {\n %2 v;\n};\n#pragma pack(pop)\n#else\nstruct __attribute__((packed)) %1 {\n %2 v;\n};\n#endif")
  106. io.replace("cutils_host.h", "#include <inttypes.h>", "#include <inttypes.h>\n" .. msvc_compat, {plain = true})
  107. -- Prevent redefinition of macros
  108. io.replace("cutils_host.h", "(#define likely%(x%).-)\n", "#ifndef likely\n%1\n#endif\n")
  109. io.replace("cutils_host.h", "(#define unlikely%(x%).-)\n", "#ifndef unlikely\n%1\n#endif\n")
  110. io.replace("cutils_host.h", "(#define force_inline.-)\n", "#ifndef force_inline\n%1\n#endif\n")
  111. io.replace("cutils_host.h", "(#define no_inline.-)\n", "#ifndef no_inline\n%1\n#endif\n")
  112. io.replace("cutils_host.h", "(#define __maybe_unused.-)\n", "#ifndef __maybe_unused\n%1\n#endif\n")
  113. end
  114. if package:is_plat("windows", "mingw") then
  115. -- Fix packed structs
  116. io.replace("cutils.h", "struct __attribute__%(%(packed%)%) (packed_u%d+) {\n%s+(uint%d+_t) v;\n};",
  117. "#ifdef _MSC_VER\n#pragma pack(push, 1)\nstruct %1 {\n %2 v;\n};\n#pragma pack(pop)\n#else\nstruct __attribute__((packed)) %1 {\n %2 v;\n};\n#endif")
  118. io.replace("cutils.h", "#include <inttypes.h>", "#include <inttypes.h>\n" .. msvc_compat, {plain = true})
  119. -- Prevent redefinition of macros
  120. io.replace("cutils.h", "(#define likely%(x%).-)\n", "#ifndef likely\n%1\n#endif\n")
  121. io.replace("cutils.h", "(#define unlikely%(x%).-)\n", "#ifndef unlikely\n%1\n#endif\n")
  122. io.replace("cutils.h", "(#define force_inline.-)\n", "#ifndef force_inline\n%1\n#endif\n")
  123. io.replace("cutils.h", "(#define no_inline.-)\n", "#ifndef no_inline\n%1\n#endif\n")
  124. io.replace("cutils.h", "(#define __maybe_unused.-)\n", "#ifndef __maybe_unused\n%1\n#endif\n")
  125. -- Fix sys/time.h include in dtoa.c and other files
  126. io.replace("dtoa.c", "#include <sys/time.h>", "#ifndef _MSC_VER\n#include <sys/time.h>\n#endif", {plain = true})
  127. io.replace("mquickjs.c", "#include <sys/time.h>", "#ifndef _MSC_VER\n#include <sys/time.h>\n#endif", {plain = true})
  128. io.replace("libm.c", "#include <sys/time.h>", "#ifndef _MSC_VER\n#include <sys/time.h>\n#endif", {plain = true})
  129. io.replace("mqjs.c", "#include <sys/time.h>", "#ifndef _MSC_VER\n#include <sys/time.h>\n#endif", {plain = true})
  130. io.replace("readline_tty.c", "#include <sys/time.h>", "#ifndef _MSC_VER\n#include <sys/time.h>\n#endif", {plain = true})
  131. -- Fix void* arithmetic in mquickjs.c
  132. io.replace("mquickjs.c", "ctx->stack_top = mem_start + mem_size;", "ctx->stack_top = (uint8_t *)mem_start + mem_size;", {plain = true})
  133. -- Fix dump_token attribute syntax
  134. io.replace("mquickjs.c", "__attribute((unused)) dump_token", "dump_token", {plain = true})
  135. -- Fix case range syntax (GCC extension)
  136. io.replace("mquickjs.c", "case 'a' ... 'z':", "case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x': case 'y': case 'z':", {plain = true})
  137. io.replace("mquickjs.c", "case 'A' ... 'Z':", "case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z':", {plain = true})
  138. io.replace("mquickjs.c", "case '1' ... '9':", "case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':", {plain = true})
  139. io.replace("readline.c", "case '0' ... '9':", "case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':", {plain = true})
  140. -- Fix incomplete array type for parse_func_table
  141. io.replace("mquickjs.c", "static JSParseFunc *parse_func_table[];", "static JSParseFunc *parse_func_table[16];", {plain = true})
  142. -- Fix void function returning value warnings/errors
  143. io.replace("mquickjs.c", "return JS_PrintValueF(ctx, val, 0);", "JS_PrintValueF(ctx, val, 0);", {plain = true})
  144. io.replace("mquickjs.c", "return js_parse_error(s, \"not enough memory\");", "js_parse_error(s, \"not enough memory\");", {plain = true})
  145. io.replace("mquickjs.c", "return js_parse_error(s, \"stack overflow\");", "js_parse_error(s, \"stack overflow\");", {plain = true})
  146. io.replace("mquickjs.c", "return js_parse_error(s, \"expecting '%c'\", c);", "js_parse_error(s, \"expecting '%c'\", c);", {plain = true})
  147. -- Fix division by zero constant expression (error C2124)
  148. io.replace("mquickjs.c", "return __JS_NewFloat64(ctx, is_max ? -1.0 / 0.0 : 1.0 / 0.0);", "return __JS_NewFloat64(ctx, is_max ? -HUGE_VAL : HUGE_VAL);", {plain = true})
  149. io.replace("mquickjs.c", "return __JS_NewFloat64(ctx, is_max ? -INFINITY : INFINITY);", "return __JS_NewFloat64(ctx, is_max ? -HUGE_VAL : HUGE_VAL);", {plain = true})
  150. end
  151. io.writefile("xmake.lua", [[
  152. add_rules("mode.release", "mode.debug")
  153. set_policy("build.fence", true)
  154. target("mqjs_stdlib_gen")
  155. set_kind("binary")
  156. set_plat(os.host())
  157. set_arch(os.arch())
  158. add_files("mqjs_stdlib.c", "mquickjs_build.c")
  159. add_defines("_GNU_SOURCE")
  160. target("mquickjs")
  161. set_kind("$(kind)")
  162. add_deps("mqjs_stdlib_gen")
  163. add_files("mquickjs.c", "libm.c", "dtoa.c", "cutils.c")
  164. add_headerfiles("mquickjs.h")
  165. add_defines("_GNU_SOURCE")
  166. if is_plat("windows") and is_kind("shared") then
  167. add_rules("utils.symbols.export_all", {export_classes = true})
  168. end
  169. if is_plat("linux", "macosx", "bsd") then
  170. add_syslinks("m")
  171. end
  172. before_build(function (target)
  173. local mqjs_stdlib_gen = target:dep("mqjs_stdlib_gen"):targetfile()
  174. local flags = {}
  175. if not target:is_arch64() then
  176. table.insert(flags, "-m32")
  177. end
  178. os.vrunv(mqjs_stdlib_gen, table.join({"-a"}, flags), {stdout = "mquickjs_atom.h"})
  179. os.vrunv(mqjs_stdlib_gen, flags, {stdout = "mqjs_stdlib.h"})
  180. -- Patch mqjs_stdlib.h for MSVC compatibility
  181. if is_plat("windows", "mingw") then
  182. io.replace("mqjs_stdlib.h", "static const uint64_t __attribute%(%(aligned%(64%)%)%) js_stdlib_table%[%]", "__declspec(align(64)) static const uint64_t js_stdlib_table[]", {plain = false})
  183. io.replace("mqjs_stdlib.h", "static const uint32_t __attribute%(%(aligned%(64%)%)%) js_stdlib_table%[%]", "__declspec(align(64)) static const uint32_t js_stdlib_table[]", {plain = false})
  184. -- Fix zero-sized array error (C2466)
  185. io.replace("mqjs_stdlib.h", "static const JSCFinalizer js_c_finalizer_table[JS_CLASS_COUNT - JS_CLASS_USER] = {", "static const JSCFinalizer js_c_finalizer_table[(JS_CLASS_COUNT - JS_CLASS_USER) > 0 ? (JS_CLASS_COUNT - JS_CLASS_USER) : 1] = {", {plain = true})
  186. -- Fix missing Windows headers and sleep function in mqjs.c
  187. io.replace("mqjs.c", "#include <fcntl.h>", "#include <fcntl.h>\n#ifdef _WIN32\n#include <windows.h>\n#endif", {plain = true})
  188. io.replace("mqjs.c", "nanosleep(&ts, NULL);", "#ifdef _WIN32\nSleep(min_delay);\n#else\nnanosleep(&ts, NULL);\n#endif", {plain = true})
  189. if is_plat("windows") then
  190. -- Export utf8 helper functions in cutils.c
  191. io.replace("cutils.c", "size_t __unicode_to_utf8(uint8_t *buf, unsigned int c)", "__declspec(dllexport) size_t __unicode_to_utf8(uint8_t *buf, unsigned int c)", {plain = true})
  192. io.replace("cutils.c", "int __utf8_get(const uint8_t *p, size_t *plen)", "__declspec(dllexport) int __utf8_get(const uint8_t *p, size_t *plen)", {plain = true})
  193. -- Add dllexport to declarations in cutils.h to match definitions
  194. io.replace("cutils.h", "size_t __unicode_to_utf8(uint8_t *buf, unsigned int c);", "__declspec(dllexport) size_t __unicode_to_utf8(uint8_t *buf, unsigned int c);", {plain = true})
  195. io.replace("cutils.h", "int __utf8_get(const uint8_t *p, size_t *plen);", "__declspec(dllexport) int __utf8_get(const uint8_t *p, size_t *plen);", {plain = true})
  196. end
  197. end
  198. end)
  199. target("mqjs")
  200. set_kind("binary")
  201. add_files("mqjs.c", "readline.c")
  202. add_files("readline_tty.c")
  203. add_deps("mquickjs")
  204. add_deps("mqjs_stdlib_gen")
  205. add_includedirs(".")
  206. ]])
  207. local configs = {}
  208. import("package.tools.xmake").install(package, configs)
  209. end)
  210. on_test(function (package)
  211. assert(package:has_cfuncs("JS_NewContext", {includes = "mquickjs.h"}))
  212. if not package:is_cross() then
  213. os.vrun("mqjs -e \"var a = 1; console.log(a);\"")
  214. end
  215. end)