Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

474 lines
15 KiB

  1. // RULE.C -- routines that have to do with inference rules
  2. //
  3. // Copyright (c) 1988-1991, Microsoft Corporation. All rights reserved.
  4. //
  5. // Purpose:
  6. // Routines that have to do with inference rules
  7. //
  8. // Revision History:
  9. // 04-Feb-2000 BTF Ported to Win64
  10. // 15-Nov-1993 JdR Major speed improvements
  11. // 15-Oct-1993 HV Use tchar.h instead of mbstring.h directly, change STR*() to _ftcs*()
  12. // 10-May-1993 HV Add include file mbstring.h
  13. // Change the str* functions to STR*
  14. // 16-May-1991 SB Created using routines from other modules
  15. #include "precomp.h"
  16. #pragma hdrstop
  17. #define PUBLIC
  18. extern char * QueryFileInfo(char *, void **);
  19. BOOL removeDuplicateRules(RULELIST*, RULELIST*);
  20. char * skipPathList(char*);
  21. // findRule -- finds the implicit rule which can be used to build a target
  22. //
  23. // Scope: Global
  24. //
  25. // Purpose:
  26. // Given a target findRule() finds if an implicit rule exists to create this
  27. // target. It does this by scanning the extensions in the list of rules.
  28. //
  29. // Input:
  30. // name -- the name of the file corresponding to the rule (see Notes)
  31. // target -- the target to be built
  32. // ext -- the extension of the target
  33. // dBuf -- a pointer to the file information about name
  34. //
  35. // Output:
  36. // Returns a pointer to the applicable rule (NULL if none is found)
  37. // On return dBuf points to the fileInfo of the file corresponding
  38. // to the applicable inference rule. (see Notes)
  39. //
  40. // Assumes:
  41. // It assumes that name points to a buffer of size MAXNAME of allocated memory
  42. // and dBuf points to an allocated memory area corr to the size of FILEINFO.
  43. //
  44. // Modifies Globals:
  45. // global -- how/what
  46. //
  47. // Uses Globals:
  48. // rules -- the list of implicit rules
  49. //
  50. // Notes:
  51. // Once NMAKE finds a rule for the extension it looks for the file with the same
  52. // base name as the target and an extension which is part of the rule. This
  53. // file is the file corresponding to the rule. Only when this file exists does
  54. // NMAKE consider the inference rule to be applicable. This file is returned
  55. // in name and dBuf points to the information about this file.
  56. // It handles quotes in filenames too.
  57. RULELIST *
  58. findRule(
  59. char *name,
  60. char *target,
  61. char *ext,
  62. void *dBuf
  63. )
  64. {
  65. RULELIST *r; // pointer to rule
  66. char *s, // name of rule
  67. *ptrToExt; // extension
  68. char *endPath, *ptrToTarg, *ptrToName, *temp;
  69. int n, m;
  70. MAKEOBJECT *object = NULL;
  71. for (r = rules; r; r = r->next) {
  72. s = r->name;
  73. #ifdef DEBUG_ALL
  74. printf("* findRule: %s,\n", r->name);
  75. DumpList(r->buildCommands);
  76. DumpList(r->buildMacros);
  77. #endif
  78. ptrToExt = _tcsrchr(s, '.');
  79. // Compare ignoring enclosing quotes
  80. if (!strcmpiquote(ptrToExt, ext)) {
  81. *name = '\0';
  82. for (ptrToTarg = (s+1); *ptrToTarg && *ptrToTarg != '{';ptrToTarg = _tcsinc(ptrToTarg))
  83. if (*ptrToTarg == ESCH)
  84. ptrToTarg++;
  85. // If Quotes present skip to end-quote
  86. else if (*ptrToTarg == '"')
  87. for (ptrToTarg++; *ptrToTarg != '"'; ptrToTarg++)
  88. ;
  89. if (*ptrToTarg) {
  90. for (endPath = ptrToTarg; *endPath && *endPath != '}';endPath = _tcsinc(endPath))
  91. if (*endPath == ESCH)
  92. endPath++;
  93. n = (int) (endPath - (ptrToTarg + 1));
  94. // ignore leading quote on target
  95. temp = target;
  96. if (*temp == '"')
  97. temp++;
  98. for (ptrToExt = ptrToTarg+1; n; n -= (int) _tclen(ptrToExt),
  99. ptrToExt = _tcsinc(ptrToExt),
  100. temp = _tcsinc(temp)) { // compare paths
  101. if (*ptrToExt == '\\' || *ptrToExt == '/') {
  102. if (*temp != '\\' && *temp != '/') {
  103. n = -1;
  104. break;
  105. }
  106. } else if (_tcsnicmp(ptrToExt, temp, _tclen(ptrToExt))) {
  107. n = -1;
  108. break;
  109. }
  110. }
  111. if (n == -1)
  112. continue; // match failed; do next rule
  113. ptrToExt = ptrToTarg;
  114. n = (int) (endPath - (ptrToTarg + 1));
  115. char *pchLast = _tcsdec(ptrToTarg, endPath);
  116. ptrToName = target + n + 1; // if more path
  117. if (((temp = _tcschr(ptrToName, '\\')) // left in target (we
  118. || (temp = _tcschr(ptrToName, '/'))) // let separator in
  119. && (temp != ptrToName // target path in rule,
  120. || *pchLast == '\\' // e.g. .c.{\x}.obj
  121. || *pchLast == '/')) // same as .c.{\x\}.obj)
  122. continue; // use dependent's path,
  123. } // not target's
  124. if (*s == '{') {
  125. for (endPath = ++s; *endPath && *endPath != '}'; endPath = _tcsinc (endPath))
  126. if (*endPath == ESCH)
  127. endPath++;
  128. n = (int) (endPath - s);
  129. if (n) {
  130. _tcsncpy(name, s, n);
  131. s += n + 1; // +1 to go past '}'
  132. if (*(s-2) != '\\')
  133. *(name+n++) = '\\';
  134. } else {
  135. if (*target == '"')
  136. _tcsncpy(name, "\".\\", n = 3);
  137. else
  138. _tcsncpy(name, ".\\", n = 2);
  139. s += 1;
  140. }
  141. ptrToName = _tcsrchr(target, '\\');
  142. temp = _tcsrchr(target, '/');
  143. if (ptrToName = (temp > ptrToName) ? temp : ptrToName) {
  144. _tcscpy(name+n, ptrToName+1);
  145. n += (int) (ext - (ptrToName + 1));
  146. } else {
  147. char *szTargNoQuote = *target == '"' ? target + 1 : target;
  148. _tcscpy(name+n, szTargNoQuote);
  149. n += (int) (ext - szTargNoQuote);
  150. }
  151. } else {
  152. char *t;
  153. //if rule has path for target then strip off path part
  154. if (*ptrToTarg) {
  155. t = _tcsrchr(target, '.');
  156. while (*t != ':' && *t != '\\' && *t != '/' && t > target)
  157. t = _tcsdec(target, t);
  158. if (*t == ':' || *t == '\\' || *t == '/')
  159. t++;
  160. } else
  161. t = target;
  162. n = (int) (ext - t);
  163. // preserve the opening quote on target if stripped off path part
  164. m = 0;
  165. if ((t != target) && (*target == '"')) {
  166. *name = '"';
  167. m = 1;
  168. }
  169. _tcsncpy(name + m, t, n);
  170. n += m;
  171. }
  172. m = (int) (ptrToExt - s);
  173. if (n + m > MAXNAME) {
  174. makeError(0, NAME_TOO_LONG);
  175. }
  176. _tcsncpy(name+n, s, m); // need to be less
  177. // If quoted add a quote at the end too
  178. if (*name == '"' && *(name+n+m-1) != '"') {
  179. *(name+n+m) = '"';
  180. m++;
  181. }
  182. *(name+n+m) = '\0'; // cryptic w/ error
  183. // Call QueryFileInfo() instead of DosFindFirst() because we need
  184. // to circumvent the non-FAPI nature of DosFindFirst()
  185. if ((object = findTarget(name)) || QueryFileInfo(name, (void **)dBuf)) {
  186. if (object) {
  187. putDateTime((_finddata_t*)dBuf, object->dateTime);
  188. }
  189. return(r);
  190. }
  191. }
  192. }
  193. return(NULL);
  194. }
  195. // freeRules -- free inference rules
  196. //
  197. // Scope: Global
  198. //
  199. // Purpose: This function clears the list of inference rules presented to it.
  200. //
  201. // Input:
  202. // r -- The list of rules to be freed.
  203. // fWarn -- Warn if rules is not in .SUFFIXES
  204. //
  205. // Assumes:
  206. // That the list presented to it is a list of rules which are not needed anymore
  207. //
  208. // Uses Globals:
  209. // gFlags -- The global actions flag, to find if -p option is specified
  210. void
  211. freeRules(
  212. RULELIST *r,
  213. BOOL fWarn
  214. )
  215. {
  216. RULELIST *q;
  217. while (q = r) {
  218. if (fWarn && ON(gFlags, F1_PRINT_INFORMATION)) // if -p option specified
  219. makeError(0, IGNORING_RULE, r->name);
  220. FREE(r->name); // free name of rule
  221. freeStringList(r->buildCommands); // free command list
  222. freeStringList(r->buildMacros); // free command macros Note: free a Macro List
  223. r = r->next;
  224. FREE(q); // free rule
  225. }
  226. }
  227. BOOL
  228. removeDuplicateRules(
  229. RULELIST *newRule,
  230. RULELIST *rules
  231. )
  232. {
  233. RULELIST *r;
  234. STRINGLIST *p;
  235. for (r = rules; r; r = r->next) {
  236. if (!_tcsicmp(r->name, newRule->name)) {
  237. FREE(newRule->name);
  238. while (p = newRule->buildCommands) {
  239. newRule->buildCommands = p->next;
  240. FREE(p->text);
  241. FREE_STRINGLIST(p);
  242. }
  243. FREE(newRule);
  244. return(TRUE);
  245. }
  246. }
  247. return(FALSE);
  248. }
  249. // skipPathList -- skip any path list in string
  250. //
  251. // Scope: Local
  252. //
  253. // Purpose:
  254. // This function skips past any path list in an inference rule. A rule can have
  255. // optionally a path list enclosed in {} before the extensions. skipPathList()
  256. // checks if any path list is present and if found skips past it.
  257. //
  258. // Input: s -- rule under consideration
  259. //
  260. // Output: Returns pointer to the extension past the path list
  261. //
  262. // Assumes: That the inference rule is syntactically correct & its syntax
  263. //
  264. // Notes: The syntax of a rule is -- {toPathList}.to{fromPathList}.from
  265. char *
  266. skipPathList(
  267. char *s
  268. )
  269. {
  270. if (*s == '{') {
  271. while (*s != '}') {
  272. if (*s == ESCH)
  273. s++;
  274. s = _tcsinc(s);
  275. }
  276. s = _tcsinc(s);
  277. }
  278. return(s);
  279. }
  280. // sortRules -- sorts the list of inference rules on .SUFFIXES order
  281. //
  282. // Scope: Global
  283. //
  284. // Purpose:
  285. // This function sorts the inference rules list into an order depending on the
  286. // order in which the suffixes are listed in '.SUFFIXES'. The inference rules
  287. // which have their '.toext' part listed earlier in .SUFFIXES are reordered to
  288. // be earlier in the inference rules list. The inference rules for suffixes that
  289. // are not in .SUFFIXES are detected here and are ignored.
  290. //
  291. // Modifies Globals:
  292. // rules -- the list of rules which gets sorted
  293. //
  294. // Uses Globals:
  295. // dotSuffixList -- the list of valid suffixes for implicit inference rules.
  296. //
  297. // Notes:
  298. // The syntax of a rule is -- '{toPath}.toExt{fromPath}.fromExt'. This function
  299. // sorts the rule list into an order. Suffixes are (as of 1.10.016) checked in a
  300. // case insensitive manner.
  301. PUBLIC void
  302. sortRules(
  303. void
  304. )
  305. {
  306. STRINGLIST *p, // suffix under consideration
  307. *s,
  308. *macros = NULL;
  309. RULELIST *oldRules, // inference rule list before sort
  310. *newRules,
  311. *r; // rule under consideration in oldRules
  312. char *suff, *toExt;
  313. size_t n;
  314. oldRules = rules;
  315. rules = NULL;
  316. for (p = dotSuffixList; p; p = p->next) {
  317. n = _tcslen(suff = p->text);
  318. for (r = oldRules; r;) {
  319. toExt = skipPathList(r->name);
  320. if (!_tcsnicmp(suff, toExt, n) &&
  321. (*(toExt+n) == '.' || *(toExt+n) == '{')
  322. ) {
  323. newRules = r;
  324. if (r->back)
  325. r->back->next = r->next;
  326. else
  327. oldRules = r->next;
  328. if (r->next)
  329. r->next->back = r->back;
  330. r = r->next;
  331. newRules->next = NULL;
  332. if (!removeDuplicateRules(newRules, rules)) {
  333. for (s = newRules->buildCommands; s; s = s->next) {
  334. findMacroValuesInRule(newRules, s->text, &macros);
  335. }
  336. newRules->buildMacros = macros;
  337. macros = NULL;
  338. appendItem((STRINGLIST**)&rules, (STRINGLIST*)newRules);
  339. }
  340. } else
  341. r = r->next;
  342. }
  343. }
  344. // forget about rules whose suffixes not in .SUFFIXES
  345. if (oldRules)
  346. freeRules(oldRules, TRUE);
  347. }
  348. // useRule -- applies inference rules for a target (if possible)
  349. //
  350. // Scope: Local.
  351. //
  352. // Purpose:
  353. // When no explicit commands are available for a target NMAKE tries to use the
  354. // available inference rules. useRule() checks if an applicable inference rule
  355. // is present. If such a rule is found then it attempts a build using this rule
  356. // and if no applicable rule is present it conveys this to the caller.
  357. //
  358. // Input:
  359. // object - object under consideration
  360. // name - name of target
  361. // targetTime - time of target
  362. // qList - QuestionList for target
  363. // sList - StarStarList for target
  364. // status - is dependent available
  365. // maxDepTime - maximum time of dependent
  366. // pFirstDep - first dependent
  367. //
  368. // Output:
  369. // Returns ... applicable rule
  370. RULELIST *
  371. useRule(
  372. MAKEOBJECT *object,
  373. char *name,
  374. time_t targetTime,
  375. STRINGLIST **qList,
  376. STRINGLIST **sList,
  377. int *status,
  378. time_t *maxDepTime,
  379. char **pFirstDep
  380. )
  381. {
  382. struct _finddata_t finddata;
  383. STRINGLIST *temp;
  384. RULELIST *r;
  385. time_t tempTime;
  386. char *t;
  387. if (!(t = _tcsrchr(object->name, '.')) ||
  388. (!(r = findRule(name, object->name, t, &finddata)))
  389. ) {
  390. return(NULL); // there is NO rule applicable
  391. }
  392. tempTime = getDateTime(&finddata);
  393. *pFirstDep = name;
  394. for (temp = *sList; temp; temp = temp->next) {
  395. if (!_tcsicmp(temp->text, name)) {
  396. break;
  397. }
  398. }
  399. if (temp) {
  400. CLEAR(object->flags2, F2_DISPLAY_FILE_DATES);
  401. }
  402. *status += invokeBuild(name, object->flags2, &tempTime, NULL);
  403. if (ON(object->flags2, F2_FORCE_BUILD) ||
  404. targetTime < tempTime ||
  405. (fRebuildOnTie && (targetTime == tempTime))
  406. ) {
  407. if (!temp) {
  408. temp = makeNewStrListElement();
  409. temp->text = makeString(name);
  410. appendItem(qList, temp);
  411. if (!*sList) { // if this is the only dep found for
  412. *sList = *qList; // the target, $** list is updated
  413. }
  414. }
  415. if (ON(object->flags2, F2_DISPLAY_FILE_DATES) &&
  416. OFF(object->flags2, F2_FORCE_BUILD)
  417. ) {
  418. makeMessage(UPDATE_INFO, name, object->name);
  419. }
  420. }
  421. *maxDepTime = __max(*maxDepTime, tempTime);
  422. return(r);
  423. }