incdir.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503
  1. /****************************************************************************
  2. * tools/incdir.c
  3. *
  4. * Licensed to the Apache Software Foundation (ASF) under one or more
  5. * contributor license agreements. See the NOTICE file distributed with
  6. * this work for additional information regarding copyright ownership. The
  7. * ASF licenses this file to you under the Apache License, Version 2.0 (the
  8. * "License"); you may not use this file except in compliance with the
  9. * License. You may obtain a copy of the License at
  10. *
  11. * http://www.apache.org/licenses/LICENSE-2.0
  12. *
  13. * Unless required by applicable law or agreed to in writing, software
  14. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  15. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  16. * License for the specific language governing permissions and limitations
  17. * under the License.
  18. *
  19. ****************************************************************************/
  20. /****************************************************************************
  21. * Included Files
  22. ****************************************************************************/
  23. #include <sys/utsname.h>
  24. #include <stdbool.h>
  25. #include <stdlib.h>
  26. #include <stdarg.h>
  27. #include <unistd.h>
  28. #include <stdio.h>
  29. #include <string.h>
  30. #include <libgen.h>
  31. #include <errno.h>
  32. #ifdef HOST_CYGWIN
  33. # include <sys/cygwin.h>
  34. #endif
  35. /****************************************************************************
  36. * Private Types
  37. ****************************************************************************/
  38. enum pathtype_e
  39. {
  40. USER_PATH = 0,
  41. SYSTEM_PATH
  42. };
  43. enum os_e
  44. {
  45. OS_UNKNOWN = 0,
  46. OS_LINUX,
  47. OS_WINDOWS,
  48. OS_CYGWIN,
  49. OS_MSYS,
  50. OS_WSL,
  51. OS_MACOS,
  52. OS_BSD
  53. };
  54. enum compiler_e
  55. {
  56. COMPILER_UNKNOWN = 0,
  57. COMPILER_GCC,
  58. COMPILER_CLANG,
  59. COMPILER_MINGW,
  60. COMPILER_SDCC,
  61. COMPILER_ZDSII
  62. };
  63. /****************************************************************************
  64. * Private Functions
  65. ****************************************************************************/
  66. static void show_advice(const char *progname, int exitcode)
  67. {
  68. fprintf(stderr, "\nUSAGE: %s [-h] [-w] [-s] <compiler-path> "
  69. "<dir1> [<dir2> [<dir3> ...]]\n",
  70. progname);
  71. fprintf(stderr, "Try '%s -h' for more information\n", progname);
  72. exit(exitcode);
  73. }
  74. static void show_help(const char *progname, int exitcode)
  75. {
  76. fprintf(stderr, "%s is a tool for flexible generation of include path "
  77. "arguments for a\n",
  78. progname);
  79. fprintf(stderr, "variety of different compilers in a variety of "
  80. "compilation environments\n");
  81. fprintf(stderr, "\nUSAGE: %s [-w] [-s] <compiler-path> "
  82. "<dir1> [<dir2> [<dir3> ...]]\n",
  83. progname);
  84. fprintf(stderr, " %s -h\n\n", progname);
  85. fprintf(stderr, "Where:\n");
  86. fprintf(stderr, " <compiler-path>\n");
  87. fprintf(stderr, " The full path to your compiler\n");
  88. fprintf(stderr, " <dir1> [<dir2> [<dir3> ...]]\n");
  89. fprintf(stderr, " A list of include directories\n");
  90. fprintf(stderr, " -w\n");
  91. fprintf(stderr, " The compiler is a Windows native tool and requires "
  92. "Windows\n");
  93. fprintf(stderr, " style pathnames like C:\\Program Files\n");
  94. fprintf(stderr, " -s\n");
  95. fprintf(stderr, " Generate standard, system header file paths instead "
  96. "of normal user\n");
  97. fprintf(stderr, " header file paths.\n");
  98. fprintf(stderr, " -h\n");
  99. fprintf(stderr, " Shows this help text and exits.\n");
  100. exit(exitcode);
  101. }
  102. static enum os_e get_os(char *ccname)
  103. {
  104. struct utsname buf;
  105. int ret;
  106. /* Check for MinGW which implies a Windows native environment */
  107. if (strstr(ccname, "mingw") != NULL)
  108. {
  109. return OS_WINDOWS;
  110. }
  111. /* Get the context names */
  112. ret = uname(&buf);
  113. if (ret < 0)
  114. {
  115. int errcode = errno;
  116. fprintf(stderr, "ERROR: uname failed: %s\n", strerror(errcode));
  117. exit(EXIT_FAILURE);
  118. }
  119. if (strcmp(buf.sysname, "Linux") == 0)
  120. {
  121. return OS_LINUX; /* Or OS_WSL */
  122. }
  123. else if (strncmp(buf.sysname, "CYGWIN", 6) == 0)
  124. {
  125. return OS_CYGWIN;
  126. }
  127. else if (strncmp(buf.sysname, "MINGW", 5) == 0)
  128. {
  129. return OS_CYGWIN;
  130. }
  131. else if (strncmp(buf.sysname, "MSYS", 4) == 0)
  132. {
  133. return OS_CYGWIN;
  134. }
  135. else if (strcmp(buf.sysname, "Darwin") == 0)
  136. {
  137. return OS_MACOS;
  138. }
  139. else if (strcmp(buf.sysname, "FreeBSD") == 0 ||
  140. strcmp(buf.sysname, "OpenBSD") == 0 ||
  141. strcmp(buf.sysname, "GNU/kFreeBSD") == 0)
  142. {
  143. return OS_BSD;
  144. }
  145. else
  146. {
  147. fprintf(stderr, "ERROR: Unknown operating system: %s\n",
  148. buf.sysname);
  149. return OS_UNKNOWN;
  150. }
  151. }
  152. static enum compiler_e get_compiler(char *ccname, enum os_e os)
  153. {
  154. /* Let's assume that all GCC compiler paths contain the string gcc or
  155. * g++ and no non-GCC compiler paths include these substrings.
  156. *
  157. * If the compiler is called cc, let's assume that is GCC too.
  158. */
  159. if (strstr(ccname, "gcc") != NULL ||
  160. strstr(ccname, "g++") != NULL ||
  161. strncmp(ccname, "cc.", 3) == 0)
  162. {
  163. return COMPILER_GCC;
  164. }
  165. else if (strstr(ccname, "clang") != NULL)
  166. {
  167. return COMPILER_CLANG;
  168. }
  169. else if (strstr(ccname, "sdcc") != NULL)
  170. {
  171. return COMPILER_SDCC;
  172. }
  173. else if (strstr(ccname, "mingw") != NULL)
  174. {
  175. return COMPILER_MINGW;
  176. }
  177. else if (strstr(ccname, "ez8cc") != NULL ||
  178. strstr(ccname, "zneocc") != NULL ||
  179. strstr(ccname, "ez80cc") != NULL)
  180. {
  181. return COMPILER_ZDSII;
  182. }
  183. else
  184. {
  185. /* Unknown compiler. Assume GCC-compatible */
  186. return COMPILER_GCC;
  187. }
  188. }
  189. static int my_asprintf(char **strp, const char *fmt, ...)
  190. {
  191. va_list ap;
  192. ssize_t bufsize;
  193. char *buffer;
  194. /* Get the size of the buffer */
  195. va_start(ap, fmt);
  196. bufsize = vsnprintf(NULL, 0, fmt, ap);
  197. va_end(ap);
  198. if (bufsize <= 0)
  199. {
  200. fprintf(stderr, "ERROR: vsnprintf() failed.\n");
  201. exit (EXIT_FAILURE);
  202. }
  203. buffer = malloc(bufsize + 1);
  204. if (buffer == NULL)
  205. {
  206. fprintf(stderr, "ERROR: Failed allocated vsnprintf() buffer.\n");
  207. exit (EXIT_FAILURE);
  208. }
  209. va_start(ap, fmt);
  210. vsnprintf(buffer, bufsize + 1, fmt, ap);
  211. va_end(ap);
  212. *strp = buffer;
  213. return bufsize;
  214. }
  215. /****************************************************************************
  216. * Public Functions
  217. ****************************************************************************/
  218. int main(int argc, char **argv, char **envp)
  219. {
  220. #ifdef HOST_CYGWIN
  221. char *convpath = NULL;
  222. bool wintool = false;
  223. #endif
  224. enum pathtype_e pathtype = USER_PATH;
  225. enum os_e os;
  226. enum compiler_e compiler;
  227. const char *progname = argv[0];
  228. const char *cmdarg;
  229. char *ccname;
  230. char * const *dirlist;
  231. size_t respsize = 0;
  232. char *response = NULL;
  233. int ndirs;
  234. int ret;
  235. int ch;
  236. int i;
  237. /* Handle command line options */
  238. while ((ch = getopt(argc, argv, "wsh")) >= 0)
  239. {
  240. switch (ch)
  241. {
  242. case 'w':
  243. #ifdef HOST_CYGWIN
  244. wintool = true;
  245. #endif
  246. break;
  247. case 's':
  248. pathtype = SYSTEM_PATH;
  249. break;
  250. case 'h':
  251. show_help(progname, EXIT_SUCCESS);
  252. }
  253. }
  254. if (optind >= argc)
  255. {
  256. fprintf(stderr, "ERROR: Missing <compiler-path>\n");
  257. show_advice(progname, EXIT_FAILURE);
  258. }
  259. ccname = basename(argv[optind]);
  260. optind++;
  261. if (optind >= argc)
  262. {
  263. fprintf(stderr, "ERROR: At least one directory must be supplied\n");
  264. show_advice(progname, EXIT_FAILURE);
  265. }
  266. dirlist = &argv[optind];
  267. ndirs = argc - optind;
  268. /* Most compilers support CFLAG options like '-I<dir>' to add include
  269. * file header paths. Some (like the Zilog tools), do not. This script
  270. * makes the selection of header file paths compiler independent.
  271. *
  272. * Below are all known compiler names (as found in the board/ Make.defs
  273. * files). If a new compiler is used that has some unusual syntax, then
  274. * additional logic needs to be added to this file.
  275. *
  276. * NAME Syntax
  277. * $(CROSSDEV)gcc -I<dir1> -I<dir2> -I<dir3> ...
  278. * sdcc -I<dir2> -I<dir2> -I<dir3> ...
  279. * $(ZDSBINDIR)/ez8cc.exe -usrinc:'<dir1>:<dir2>:<dir3>:...`
  280. * $(ZDSBINDIR)/zneocc.exe -usrinc:'<dir1>:<dir2>:<dir3>:...`
  281. * $(ZDSBINDIR)/ez80cc.exe -usrinc:'<dir1>:<dir2>:<dir3>:...`
  282. *
  283. * Furthermore, just to make matters more difficult, with Windows based
  284. * toolchains, we have to use the full windows-style paths to the header
  285. * files.
  286. */
  287. os = get_os(ccname);
  288. if (os == OS_UNKNOWN)
  289. {
  290. fprintf(stderr, "ERROR: Operating system not recognized\n");
  291. show_advice(progname, EXIT_FAILURE);
  292. }
  293. compiler = get_compiler(ccname, os);
  294. if (compiler == COMPILER_UNKNOWN)
  295. {
  296. fprintf(stderr, "ERROR: Compiler not recognized.\n");
  297. show_advice(progname, EXIT_FAILURE);
  298. }
  299. /* Select system or user header file path command line option */
  300. if (compiler == COMPILER_ZDSII)
  301. {
  302. cmdarg = (pathtype == SYSTEM_PATH) ? "-stdinc:" : "-usrinc:";
  303. #ifdef HOST_CYGWIN
  304. wintool = true;
  305. #endif
  306. }
  307. else
  308. {
  309. cmdarg = (pathtype == SYSTEM_PATH) ? "-isystem" : "-I";
  310. }
  311. /* Now process each directory in the directory list */
  312. for (i = 0; i < ndirs; i++)
  313. {
  314. const char *dirname;
  315. const char *incpath;
  316. char *saveresp;
  317. char *segment = NULL;
  318. size_t segsize;
  319. dirname = dirlist[i];
  320. #ifdef HOST_CYGWIN
  321. /* Check if the path needs to be extended for Windows-based tools under
  322. * Cygwin:
  323. *
  324. * wintool == true: The platform is Cygwin and we are using a windows
  325. * native tool
  326. */
  327. if (os == OS_CYGWIN && wintool)
  328. {
  329. ssize_t bufsize;
  330. bufsize = cygwin_conv_path(CCP_POSIX_TO_WIN_A, dirname, NULL, 0);
  331. convpath = (char *)malloc(bufsize);
  332. if (convpath == NULL)
  333. {
  334. fprintf(stderr, "ERROR: Failed to allocate buffer.\n");
  335. exit(EXIT_FAILURE);
  336. }
  337. (void)cygwin_conv_path(CCP_POSIX_TO_WIN_A, dirname, convpath,
  338. bufsize);
  339. incpath = convpath;
  340. }
  341. else
  342. #endif
  343. {
  344. incpath = dirname;
  345. }
  346. /* Handle the output using the selected format */
  347. if (compiler == COMPILER_ZDSII)
  348. {
  349. /* FORM: -stdinc:'dir1;dir2;...;dirN'
  350. * -usrinc:'dir1;dir2;...;dirN'
  351. */
  352. /* Treat the first directory differently */
  353. if (response == NULL)
  354. {
  355. if (i == ndirs - 1)
  356. {
  357. ret = my_asprintf(&segment, "%s'%s'", cmdarg, incpath);
  358. }
  359. else
  360. {
  361. ret = my_asprintf(&segment, "%s'%s", cmdarg, incpath);
  362. }
  363. }
  364. else
  365. {
  366. if (i == ndirs - 1)
  367. {
  368. ret = my_asprintf(&segment, ";%s'", incpath);
  369. }
  370. else
  371. {
  372. ret = my_asprintf(&segment, ";%s", incpath);
  373. }
  374. }
  375. }
  376. else
  377. {
  378. /* FORM: -isystem: "dir1" -isystem "dir2" ... -isystem "dirN"
  379. * -I: "dir1" -I "dir2" ... -I "dirN"
  380. */
  381. /* Treat the first directory differently */
  382. if (response == NULL)
  383. {
  384. ret = my_asprintf(&segment, "%s \"%s\"", cmdarg, incpath);
  385. }
  386. else
  387. {
  388. ret = my_asprintf(&segment, " %s \"%s\"", cmdarg, incpath);
  389. }
  390. }
  391. if (ret < 0)
  392. {
  393. fprintf(stderr, "ERROR: my_asprintf failed.\n");
  394. exit(EXIT_FAILURE);
  395. }
  396. /* Append the new response segment */
  397. saveresp = response;
  398. segsize = ret;
  399. respsize += (response == NULL) ? segsize + 1 : segsize;
  400. response = (char *)malloc(respsize);
  401. if (response == NULL)
  402. {
  403. fprintf(stderr, "ERROR: Failed to allocate response.\n");
  404. exit(EXIT_FAILURE);
  405. }
  406. if (saveresp == NULL)
  407. {
  408. strncpy(response, segment, respsize);
  409. }
  410. else
  411. {
  412. snprintf(response, respsize, "%s%s", saveresp, segment);
  413. }
  414. /* Clean up for the next pass */
  415. if (saveresp != NULL)
  416. {
  417. free(saveresp);
  418. }
  419. if (segment != NULL)
  420. {
  421. free(segment);
  422. segment = NULL;
  423. }
  424. #ifdef HOST_CYGWIN
  425. if (convpath != NULL)
  426. {
  427. free(convpath);
  428. convpath = NULL;
  429. }
  430. #endif
  431. }
  432. fputs(response, stdout);
  433. free(response);
  434. return EXIT_SUCCESS;
  435. }