copy_src.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. import os
  4. import sys
  5. from shutil import Error, copy2, rmtree
  6. import subprocess
  7. from collections import defaultdict
  8. all_vars = set()
  9. all_funcs_with_statics = defaultdict(list)
  10. thread_funcs = []
  11. define_for_yylval = None
  12. skip_func = False
  13. erase_func = False
  14. split_def = False
  15. def_type = None
  16. def_var = None
  17. ignore_func = False
  18. inside_func = None
  19. to_add_const = set([
  20. "nullSemAction",
  21. "sentinel",
  22. "backslash_quote",
  23. "Dummy_trace",
  24. "escape_string_warning",
  25. "standard_conforming_strings",
  26. "gistBufferingOptValues",
  27. "StdRdOptIndexCleanupValues",
  28. "boolRelOpts",
  29. "intRelOpts",
  30. "realRelOpts",
  31. "viewCheckOptValues",
  32. "enumRelOpts",
  33. "stringRelOpts"])
  34. source_dirs = [
  35. "postgresql/src/backend",
  36. "postgresql/src/common",
  37. "postgresql/src/include",
  38. "postgresql/src/port",
  39. "postgresql/src/timezone",
  40. ]
  41. def is_inside_source_dirs(filename):
  42. for dir in source_dirs:
  43. if filename.startswith(dir):
  44. return True
  45. return False
  46. no_copy_sources = [
  47. "postgresql/src/include/port/win32/sys/un.h",
  48. "postgresql/src/include/port/win32/netinet/tcp.h",
  49. "postgresql/src/include/port/win32/sys/resource.h",
  50. "postgresql/src/include/port/win32/sys/select.h",
  51. "postgresql/src/include/port/win32/dlfcn.h",
  52. ]
  53. def need_copy(filename):
  54. if not is_inside_source_dirs(filename):
  55. return False
  56. for prefix in no_copy_sources:
  57. if filename.startswith(prefix):
  58. return False
  59. return True
  60. exclude_from_source_list = set([
  61. # platform-specific, explicitly added in ya.make
  62. "postgresql/src/port/pg_crc32c_sse42.c",
  63. "postgresql/src/port/pg_crc32c_sse42_choose.c",
  64. "postgresql/src/backend/port/posix_sema.c",
  65. "postgresql/src/backend/port/sysv_shmem.c",
  66. "postgresql/src/port/strlcat.c",
  67. "postgresql/src/port/strlcpy.c",
  68. ])
  69. def fix_line(line, all_lines, pos):
  70. global inside_func
  71. global define_for_yylval
  72. if line.startswith("#define yylval"):
  73. define_for_yylval=line[14:].strip()
  74. if line.startswith("#define HAVE_EXECINFO_H 1"):
  75. return "#undef HAVE_EXECINFO_H\n"
  76. if line.startswith("#define HAVE_BACKTRACE_SYMBOLS 1"):
  77. return "#undef HAVE_BACKTRACE_SYMBOLS\n"
  78. if "static YYSTYPE yyval_default" in line or \
  79. "static YYLTYPE yyloc_default" in line:
  80. return line.replace("static","static __thread")
  81. global skip_func
  82. global erase_func
  83. if line.startswith("build_guc_variables(void)"):
  84. skip_func = True
  85. return line
  86. global ignore_func
  87. if line.startswith("yyparse"):
  88. ignore_func = True
  89. return line
  90. if inside_func is not None:
  91. for v in all_funcs_with_statics[inside_func]:
  92. if v in line and "static" in line:
  93. return line.replace("static","static __thread")
  94. if inside_func:
  95. if line.startswith("}"):
  96. inside_func=None
  97. if skip_func:
  98. if line.startswith("{"):
  99. return None if erase_func else line
  100. if not line.startswith("}"):
  101. return None
  102. skip_func=False
  103. if erase_func:
  104. erase_func=False
  105. return None
  106. if ignore_func:
  107. if line.startswith("{"):
  108. return line
  109. if not line.startswith("}"):
  110. return line
  111. ignore_func=False
  112. global split_def
  113. global def_type
  114. global def_var
  115. if line.startswith("static struct xllist"):
  116. split_def = True
  117. def_type = "xllist"
  118. def_var = "records";
  119. return "typedef struct xllist\n";
  120. if line.startswith("static struct RELCACHECALLBACK"):
  121. split_def = True
  122. def_type = "RELCACHECALLBACK"
  123. def_var = "relcache_callback_list[MAX_RELCACHE_CALLBACKS]";
  124. return "typedef struct RELCACHECALLBACK\n";
  125. if line.startswith("static struct SYSCACHECALLBACK"):
  126. split_def = True
  127. def_type = "SYSCACHECALLBACK"
  128. def_var = "syscache_callback_list[MAX_SYSCACHE_CALLBACKS]";
  129. return "typedef struct SYSCACHECALLBACK\n";
  130. if split_def and line.startswith("}"):
  131. split_def = False;
  132. return "} " + def_type + "; static __thread " + def_type + " " + def_var + ";\n"
  133. if line.strip()=="static struct":
  134. i = pos
  135. while i < len(all_lines):
  136. if all_lines[i].startswith("}"):
  137. name = all_lines[i][1:].replace(";","").strip()
  138. split_def = True
  139. def_type = name + "_t"
  140. def_var = name
  141. return "typedef struct " + def_type + "\n";
  142. i += 1
  143. if "ConfigureNames" in line and line.strip().endswith("[] ="):
  144. skip_func = True
  145. erase_func = True
  146. return None
  147. if line.startswith("#") or line.startswith(" ") or line.startswith("\t"):
  148. return line
  149. for f in all_funcs_with_statics:
  150. if f in line and ";" not in line:
  151. inside_func = f
  152. return line
  153. if not "=" in line:
  154. line2=line
  155. if "//" in line2: line2 = line2[:line2.find("//")]
  156. if "/*" in line2: line2 = line2[:line2.find("/*")]
  157. if "(" in line2 or "{" in line2 or "}" in line2:
  158. return line
  159. if ";" not in line2:
  160. return line
  161. if line.startswith("YYSTYPE yylval;"):
  162. line = line.replace("yylval", define_for_yylval)
  163. norm = line.replace("\t"," ")
  164. ret = None
  165. found_v = None
  166. for v in to_add_const:
  167. if v in norm:
  168. ret = line \
  169. .replace("static","static const") \
  170. .replace("relopt_enum_elt_def","const relopt_enum_elt_def")
  171. if v == "backslash_quote":
  172. ret = ret.replace("int","const int")
  173. if v == "escape_string_warning" or v == "standard_conforming_strings":
  174. ret = ret.replace("bool","const bool")
  175. if v == "nullSemAction":
  176. ret = ret.replace("JsonSemAction","const JsonSemAction")
  177. return ret
  178. for v in all_vars:
  179. if " " + v + " " in norm or " " + v + ";" in norm or " " + v + "[" in norm or \
  180. "*" + v + " " in norm or "*" + v + ";" in norm or "*" + v + "[" in norm:
  181. found_v = v
  182. if line.startswith("static"):
  183. ret = "static __thread" + line[6:]
  184. elif line.startswith("extern"):
  185. ret = "extern __thread" + line[6:]
  186. else:
  187. ret = "__thread " + line
  188. break
  189. if ret is None:
  190. return line
  191. if "DLIST_STATIC_INIT" in ret:
  192. # rewrite without {{}} inits
  193. pos=ret.find("=");
  194. ret=ret[:pos] + ";";
  195. ret+="void "+found_v+"_init(void) { dlist_init(&" + found_v + "); }";
  196. ret+="\n";
  197. thread_funcs.append(found_v+"_init");
  198. if "DCLIST_STATIC_INIT" in ret:
  199. # rewrite without {{}} inits
  200. pos=ret.find("=");
  201. ret=ret[:pos] + ";";
  202. ret+="void "+found_v+"_init(void) { dlist_init(&" + found_v + ".dlist); " + found_v + ".count = 0; }";
  203. ret+="\n";
  204. thread_funcs.append(found_v+"_init");
  205. if "CurrentTransactionState" in ret or "mainrdata_last" in ret:
  206. # rewrite with address of TLS var
  207. pos=ret.find("=");
  208. init_val=ret[pos+1:];
  209. ret=ret[:pos] + ";";
  210. ret+="void "+found_v+"_init(void) { "+found_v+"="+init_val +" };"
  211. ret+="\n";
  212. thread_funcs.append(found_v+"_init");
  213. return ret
  214. def mycopy2(src, dst):
  215. global define_for_yylval
  216. define_for_yylval = None
  217. if not (src.endswith(".h") or src.endswith(".c")):
  218. return
  219. with open(src,"r") as fsrc:
  220. with open(dst,"w") as fdst:
  221. all_lines = list(fsrc)
  222. for pos,line in enumerate(all_lines):
  223. line = fix_line(line,all_lines,pos)
  224. if line is not None:
  225. fdst.write(line)
  226. def copy_and_patch_sources(src_dir):
  227. errors = []
  228. with open(os.path.join(src_dir, "src_files"), "r") as fd:
  229. for line in fd:
  230. name = line.strip()
  231. if not need_copy(name):
  232. continue
  233. srcname = os.path.join(src_dir, name)
  234. if name == "postgresql/src/include/pg_config.h":
  235. dstname = "postgresql/src/include/pg_config-linux.h"
  236. else:
  237. dstname = name
  238. try:
  239. os.makedirs(os.path.dirname(dstname), mode=0o755, exist_ok=True)
  240. if os.path.islink(srcname):
  241. target_full = os.path.realpath(srcname)
  242. target = os.path.relpath(target_full, start=os.path.realpath(os.path.dirname(srcname)))
  243. with open(dstname, "w") as f:
  244. print('#include "' + target + '" /* inclink generated by yamaker */', file=f)
  245. else:
  246. mycopy2(srcname, dstname)
  247. except OSError as why:
  248. errors.append((srcname, dstname, str(why)))
  249. except Error as err:
  250. errors.extend(err.args[0])
  251. if errors:
  252. raise Error(errors)
  253. def make_sources_list(build_dir):
  254. with open(f"{build_dir}/src_files","r") as fsrc:
  255. with open("pg_sources.inc","w") as fdst:
  256. fdst.write("SRCS(\n")
  257. for line in fsrc:
  258. #print(line.strip())
  259. name = line.strip()
  260. if name.endswith(".funcs.c"): continue
  261. if name.endswith(".switch.c"): continue
  262. basename = os.path.basename(name)
  263. if basename.startswith("regc_") and basename.endswith(".c"): continue
  264. if basename == "rege_dfa.c": continue
  265. if name.endswith(".c") and need_copy(name) and name not in exclude_from_source_list:
  266. fdst.write(" " + name + "\n")
  267. fdst.write(")\n")
  268. def get_vars(build_dir):
  269. s=subprocess.check_output(f"objdump {build_dir}/postgresql/src/backend/postgres.a -tw",shell=True).decode("utf-8")
  270. for a in s.replace("\t"," ").split("\n"):
  271. for b in a.split(" "):
  272. sym=None
  273. if b.startswith(".bss."): sym=b[5:]
  274. elif b.startswith(".data.") and not b.startswith(".data.rel.ro."): sym=b[6:]
  275. if sym is not None:
  276. all_vars.add(sym.replace("yql_",""))
  277. for x in to_add_const:
  278. all_vars.remove(x)
  279. all_vars.remove("BlockSig")
  280. all_vars.remove("StartupBlockSig")
  281. all_vars.remove("UnBlockSig")
  282. all_vars.add("yychar")
  283. all_vars.add("yyin")
  284. all_vars.add("yyout")
  285. all_vars.add("yyleng")
  286. all_vars.add("yynerrs")
  287. all_vars.add("yytext")
  288. all_vars.add("yy_flex_debug")
  289. all_vars.add("yylineno")
  290. with open("vars.txt","w") as f:
  291. for a in sorted(all_vars):
  292. print(a, file=f)
  293. for a in all_vars:
  294. l=a.split(".")
  295. if len(l)==2:
  296. all_funcs_with_statics[l[0]].append(l[1])
  297. def write_thread_inits():
  298. with open("thread_inits.c","w") as f:
  299. print("""#include "thread_inits.h"
  300. static __thread int pg_thread_init_flag;
  301. void pg_thread_init(void) {
  302. if (pg_thread_init_flag) return;
  303. pg_thread_init_flag=1;
  304. my_wait_event_info_init();""", file=f)
  305. for a in sorted(thread_funcs):
  306. print(" " + a + "();", file=f)
  307. print("""
  308. setup_pg_thread_cleanup();
  309. pg_timezone_initialize();
  310. }""", file=f)
  311. with open("thread_inits.h","w") as f:
  312. print("#pragma once", file=f)
  313. print("extern void pg_thread_init();", file=f)
  314. if __name__ == "__main__":
  315. if len(sys.argv) != 2:
  316. print("Usage ", sys.argv[0], " <build directory>");
  317. sys.exit(1)
  318. build_dir=sys.argv[1]
  319. get_vars(build_dir)
  320. make_sources_list(build_dir)
  321. copy_and_patch_sources(build_dir)
  322. write_thread_inits()