1 /* Dependency generator for Makefile fragments. 2 Copyright (C) 2000-2020 Free Software Foundation, Inc. 3 Contributed by Zack Weinberg, Mar 2000 4 5 This program is free software; you can redistribute it and/or modify it 6 under the terms of the GNU General Public License as published by the 7 Free Software Foundation; either version 3, or (at your option) any 8 later version. 9 10 This program is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with this program; see the file COPYING3. If not see 17 <http://www.gnu.org/licenses/>. 18 19 In other words, you are welcome to use, share and improve this program. 20 You are forbidden to forbid anyone else to use, share and improve 21 what you give them. Help stamp out software-hoarding! */ 22 23 #include "config.h" 24 #include "system.h" 25 #include "mkdeps.h" 26 27 /* Not set up to just include std::vector et al, here's a simple 28 implementation. */ 29 30 /* Keep this structure local to this file, so clients don't find it 31 easy to start making assumptions. */ 32 class mkdeps 33 { 34 public: 35 /* T has trivial cctor & dtor. */ 36 template <typename T> 37 class vec 38 { 39 private: 40 T *ary; 41 unsigned num; 42 unsigned alloc; 43 44 public: 45 vec () 46 : ary (NULL), num (0), alloc (0) 47 {} 48 ~vec () 49 { 50 XDELETEVEC (ary); 51 } 52 53 public: 54 unsigned size () const 55 { 56 return num; 57 } 58 const T &operator[] (unsigned ix) const 59 { 60 return ary[ix]; 61 } 62 T &operator[] (unsigned ix) 63 { 64 return ary[ix]; 65 } 66 void push (const T &elt) 67 { 68 if (num == alloc) 69 { 70 alloc = alloc ? alloc * 2 : 16; 71 ary = XRESIZEVEC (T, ary, alloc); 72 } 73 ary[num++] = elt; 74 } 75 }; 76 struct velt 77 { 78 const char *str; 79 size_t len; 80 }; 81 82 mkdeps () 83 : quote_lwm (0) 84 { 85 } 86 ~mkdeps () 87 { 88 unsigned int i; 89 90 for (i = targets.size (); i--;) 91 free (const_cast <char *> (targets[i])); 92 for (i = deps.size (); i--;) 93 free (const_cast <char *> (deps[i])); 94 for (i = vpath.size (); i--;) 95 XDELETEVEC (vpath[i].str); 96 } 97 98 public: 99 vec<const char *> targets; 100 vec<const char *> deps; 101 vec<velt> vpath; 102 103 public: 104 unsigned short quote_lwm; 105 }; 106 107 /* Apply Make quoting to STR, TRAIL etc. Note that it's not possible 108 to quote all such characters - e.g. \n, %, *, ?, [, \ (in some 109 contexts), and ~ are not properly handled. It isn't possible to 110 get this right in any current version of Make. (??? Still true? 111 Old comment referred to 3.76.1.) */ 112 113 static const char * 114 munge (const char *str, const char *trail = NULL, ...) 115 { 116 static unsigned alloc; 117 static char *buf; 118 unsigned dst = 0; 119 va_list args; 120 if (trail) 121 va_start (args, trail); 122 123 for (bool first = true; str; first = false) 124 { 125 unsigned slashes = 0; 126 char c; 127 for (const char *probe = str; (c = *probe++);) 128 { 129 if (alloc < dst + 4 + slashes) 130 { 131 alloc = alloc * 2 + 32; 132 buf = XRESIZEVEC (char, buf, alloc); 133 } 134 135 switch (c) 136 { 137 case '\\': 138 slashes++; 139 break; 140 141 case '$': 142 buf[dst++] = '$'; 143 goto def; 144 145 case ' ': 146 case '\t': 147 /* GNU make uses a weird quoting scheme for white space. 148 A space or tab preceded by 2N+1 backslashes 149 represents N backslashes followed by space; a space 150 or tab preceded by 2N backslashes represents N 151 backslashes at the end of a file name; and 152 backslashes in other contexts should not be 153 doubled. */ 154 while (slashes--) 155 buf[dst++] = '\\'; 156 /* FALLTHROUGH */ 157 158 case '#': 159 buf[dst++] = '\\'; 160 /* FALLTHROUGH */ 161 162 default: 163 def: 164 slashes = 0; 165 break; 166 } 167 168 buf[dst++] = c; 169 } 170 171 if (first) 172 str = trail; 173 else 174 str = va_arg (args, const char *); 175 } 176 if (trail) 177 va_end (args); 178 179 buf[dst] = 0; 180 return buf; 181 } 182 183 /* If T begins with any of the partial pathnames listed in d->vpathv, 184 then advance T to point beyond that pathname. */ 185 static const char * 186 apply_vpath (class mkdeps *d, const char *t) 187 { 188 if (unsigned len = d->vpath.size ()) 189 for (unsigned i = len; i--;) 190 { 191 if (!filename_ncmp (d->vpath[i].str, t, d->vpath[i].len)) 192 { 193 const char *p = t + d->vpath[i].len; 194 if (!IS_DIR_SEPARATOR (*p)) 195 goto not_this_one; 196 197 /* Do not simplify $(vpath)/../whatever. ??? Might not 198 be necessary. */ 199 if (p[1] == '.' && p[2] == '.' && IS_DIR_SEPARATOR (p[3])) 200 goto not_this_one; 201 202 /* found a match */ 203 t = t + d->vpath[i].len + 1; 204 break; 205 } 206 not_this_one:; 207 } 208 209 /* Remove leading ./ in any case. */ 210 while (t[0] == '.' && IS_DIR_SEPARATOR (t[1])) 211 { 212 t += 2; 213 /* If we removed a leading ./, then also remove any /s after the 214 first. */ 215 while (IS_DIR_SEPARATOR (t[0])) 216 ++t; 217 } 218 219 return t; 220 } 221 222 /* Public routines. */ 223 224 class mkdeps * 225 deps_init (void) 226 { 227 return new mkdeps (); 228 } 229 230 void 231 deps_free (class mkdeps *d) 232 { 233 delete d; 234 } 235 236 /* Adds a target T. We make a copy, so it need not be a permanent 237 string. QUOTE is true if the string should be quoted. */ 238 void 239 deps_add_target (class mkdeps *d, const char *t, int quote) 240 { 241 t = xstrdup (apply_vpath (d, t)); 242 243 if (!quote) 244 { 245 /* Sometimes unquoted items are added after quoted ones. 246 Swap out the lowest quoted. */ 247 if (d->quote_lwm != d->targets.size ()) 248 { 249 const char *lowest = d->targets[d->quote_lwm]; 250 d->targets[d->quote_lwm] = t; 251 t = lowest; 252 } 253 d->quote_lwm++; 254 } 255 256 d->targets.push (t); 257 } 258 259 /* Sets the default target if none has been given already. An empty 260 string as the default target in interpreted as stdin. The string 261 is quoted for MAKE. */ 262 void 263 deps_add_default_target (class mkdeps *d, const char *tgt) 264 { 265 /* Only if we have no targets. */ 266 if (d->targets.size ()) 267 return; 268 269 if (tgt[0] == '\0') 270 d->targets.push (xstrdup ("-")); 271 else 272 { 273 #ifndef TARGET_OBJECT_SUFFIX 274 # define TARGET_OBJECT_SUFFIX ".o" 275 #endif 276 const char *start = lbasename (tgt); 277 char *o = (char *) alloca (strlen (start) 278 + strlen (TARGET_OBJECT_SUFFIX) + 1); 279 char *suffix; 280 281 strcpy (o, start); 282 283 suffix = strrchr (o, '.'); 284 if (!suffix) 285 suffix = o + strlen (o); 286 strcpy (suffix, TARGET_OBJECT_SUFFIX); 287 288 deps_add_target (d, o, 1); 289 } 290 } 291 292 void 293 deps_add_dep (class mkdeps *d, const char *t) 294 { 295 gcc_assert (*t); 296 297 t = apply_vpath (d, t); 298 299 d->deps.push (xstrdup (t)); 300 } 301 302 void 303 deps_add_vpath (class mkdeps *d, const char *vpath) 304 { 305 const char *elem, *p; 306 307 for (elem = vpath; *elem; elem = p) 308 { 309 for (p = elem; *p && *p != ':'; p++) 310 continue; 311 mkdeps::velt elt; 312 elt.len = p - elem; 313 char *str = XNEWVEC (char, elt.len + 1); 314 elt.str = str; 315 memcpy (str, elem, elt.len); 316 str[elt.len] = '\0'; 317 if (*p == ':') 318 p++; 319 320 d->vpath.push (elt); 321 } 322 } 323 324 /* Write NAME, with a leading space to FP, a Makefile. Advance COL as 325 appropriate, wrap at COLMAX, returning new column number. Iff 326 QUOTE apply quoting. Append TRAIL. */ 327 328 static unsigned 329 make_write_name (const char *name, FILE *fp, unsigned col, unsigned colmax, 330 bool quote = true, const char *trail = NULL) 331 { 332 if (quote) 333 name = munge (name, trail, NULL); 334 unsigned size = strlen (name); 335 336 if (col) 337 { 338 if (colmax && col + size> colmax) 339 { 340 fputs (" \\\n", fp); 341 col = 0; 342 } 343 col++; 344 fputs (" ", fp); 345 } 346 347 col += size; 348 fputs (name, fp); 349 350 return col; 351 } 352 353 /* Write all the names in VEC via make_write_name. */ 354 355 static unsigned 356 make_write_vec (const mkdeps::vec<const char *> &vec, FILE *fp, 357 unsigned col, unsigned colmax, unsigned quote_lwm = 0, 358 const char *trail = NULL) 359 { 360 for (unsigned ix = 0; ix != vec.size (); ix++) 361 col = make_write_name (vec[ix], fp, col, colmax, ix >= quote_lwm, trail); 362 return col; 363 } 364 365 /* Write the dependencies to a Makefile. If PHONY is true, add 366 .PHONY targets for all the dependencies too. */ 367 368 static void 369 make_write (const class mkdeps *d, FILE *fp, bool phony, unsigned int colmax) 370 { 371 unsigned column = 0; 372 if (colmax && colmax < 34) 373 colmax = 34; 374 375 if (d->deps.size ()) 376 { 377 column = make_write_vec (d->targets, fp, 0, colmax, d->quote_lwm); 378 fputs (":", fp); 379 column++; 380 make_write_vec (d->deps, fp, column, colmax); 381 fputs ("\n", fp); 382 if (phony) 383 for (unsigned i = 1; i < d->deps.size (); i++) 384 fprintf (fp, "%s:\n", munge (d->deps[i])); 385 } 386 } 387 388 /* Write out dependencies according to the selected format (which is 389 only Make at the moment). */ 390 391 void 392 deps_write (const class mkdeps *d, FILE *fp, bool phony, unsigned int colmax) 393 { 394 make_write (d, fp, phony, colmax); 395 } 396 397 /* Write out a deps buffer to a file, in a form that can be read back 398 with deps_restore. Returns nonzero on error, in which case the 399 error number will be in errno. */ 400 401 int 402 deps_save (class mkdeps *deps, FILE *f) 403 { 404 unsigned int i; 405 size_t size; 406 407 /* The cppreader structure contains makefile dependences. Write out this 408 structure. */ 409 410 /* The number of dependences. */ 411 size = deps->deps.size (); 412 if (fwrite (&size, sizeof (size), 1, f) != 1) 413 return -1; 414 415 /* The length of each dependence followed by the string. */ 416 for (i = 0; i < deps->deps.size (); i++) 417 { 418 size = strlen (deps->deps[i]); 419 if (fwrite (&size, sizeof (size), 1, f) != 1) 420 return -1; 421 if (fwrite (deps->deps[i], size, 1, f) != 1) 422 return -1; 423 } 424 425 return 0; 426 } 427 428 /* Read back dependency information written with deps_save into 429 the deps sizefer. The third argument may be NULL, in which case 430 the dependency information is just skipped, or it may be a filename, 431 in which case that filename is skipped. */ 432 433 int 434 deps_restore (class mkdeps *deps, FILE *fd, const char *self) 435 { 436 size_t size; 437 char *buf = NULL; 438 size_t buf_size = 0; 439 440 /* Number of dependences. */ 441 if (fread (&size, sizeof (size), 1, fd) != 1) 442 return -1; 443 444 /* The length of each dependence string, followed by the string. */ 445 for (unsigned i = size; i--;) 446 { 447 /* Read in # bytes in string. */ 448 if (fread (&size, sizeof (size), 1, fd) != 1) 449 return -1; 450 451 if (size >= buf_size) 452 { 453 buf_size = size + 512; 454 buf = XRESIZEVEC (char, buf, buf_size); 455 } 456 if (fread (buf, 1, size, fd) != size) 457 { 458 XDELETEVEC (buf); 459 return -1; 460 } 461 buf[size] = 0; 462 463 /* Generate makefile dependencies from .pch if -nopch-deps. */ 464 if (self != NULL && filename_cmp (buf, self) != 0) 465 deps_add_dep (deps, buf); 466 } 467 468 XDELETEVEC (buf); 469 return 0; 470 } 471