1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 /*
  26  * @(#)rep.cc 1.25 06/12/12
  27  */
  28 
  29 #pragma ident   "@(#)rep.cc     1.25    06/12/12"
  30 
  31 /*
  32  *      rep.c
  33  *
  34  *      This file handles the .nse_depinfo file
  35  */
  36 
  37 /*
  38  * Included files
  39  */ 
  40 #include <mk/defs.h>
  41 #include <mksh/misc.h>            /* retmem() */
  42 #include <vroot/report.h> /* NSE_DEPINFO */
  43 
  44 /*
  45  * Static variables
  46  */
  47 static  Recursive_make  recursive_list;
  48 static  Recursive_make  *bpatch = &recursive_list;
  49 static  Boolean         changed;
  50 
  51 /*
  52  * File table of contents
  53  */
  54 
  55 
  56 /*
  57  *      report_recursive_init()
  58  *
  59  *      Read the .nse_depinfo file and make a list of all the
  60  *      .RECURSIVE entries.
  61  *
  62  *      Parameters:
  63  *
  64  *      Static variables used:
  65  *              bpatch          Points to slot where next cell should be added
  66  *
  67  *      Global variables used:
  68  *              recursive_name  The Name ".RECURSIVE", compared against
  69  */
  70 
  71 void
  72 report_recursive_init(void)
  73 {
  74         char            *search_dir;
  75         char            nse_depinfo[MAXPATHLEN];
  76         FILE            *fp;
  77         int             line_size, line_index;
  78         wchar_t         *line;
  79         wchar_t         *bigger_line;
  80         wchar_t         *colon;
  81         wchar_t         *dollar; 
  82         Recursive_make  rp;
  83 
  84         /*
  85          * This routine can be called more than once,  don't do
  86          * anything after the first time.
  87          */
  88         if (depinfo_already_read) {
  89                 return;
  90         } else {
  91                 depinfo_already_read = true;
  92         }
  93                                         
  94         search_dir = getenv(NOCATGETS("NSE_DEP"));
  95         if (search_dir == NULL) {
  96                 return;
  97         }
  98         (void) sprintf(nse_depinfo, "%s/%s", search_dir, NSE_DEPINFO);
  99         fp = fopen(nse_depinfo, "r");
 100         if (fp == NULL) {
 101                 return;
 102         }
 103         line_size = MAXPATHLEN;
 104         line_index = line_size - 1;
 105         line = ALLOC_WC(line_size);
 106         Wstring rns(recursive_name);
 107         wchar_t * wcb = rns.get_string();
 108         while (fgetws(line, line_size, fp) != NULL) {
 109                 while (wslen(line) == line_index) {
 110                         if (line[wslen(line) - 1] == '\n') {
 111                                 continue;
 112                         }
 113                         bigger_line = ALLOC_WC(2 * line_size);
 114                         wscpy(bigger_line, line);
 115                         retmem(line);
 116                         line = bigger_line;
 117                         if (fgetws(&line[line_index], line_size, fp) == NULL)
 118                                 continue;
 119                         line_index = 2 * line_index;
 120                         line_size = 2 * line_size;
 121                 }
 122 
 123                 colon = (wchar_t *) wschr(line, (int) colon_char);
 124                 if (colon == NULL) {
 125                         continue;
 126                 }
 127                 dollar = (wchar_t *) wschr(line, (int) dollar_char);
 128                 line[wslen(line) - 1] = (int) nul_char;
 129                 if (IS_WEQUALN(&colon[2], wcb,
 130                     (int) recursive_name->hash.length)) {
 131                         /*
 132                          * If this entry is an old entry, ignore it
 133                          */
 134                         MBSTOWCS(wcs_buffer, DEPINFO_FMT_VERSION);
 135                         if (dollar == NULL ||
 136                             !IS_WEQUALN(wcs_buffer, (dollar+1) - VER_LEN, VER_LEN)){
 137                                 continue;
 138                             }
 139                         rp = ALLOC(Recursive_make);
 140                         (void) memset((char *) rp, 0, sizeof (Recursive_make_rec));
 141                         /*
 142                          * set conditional_macro_string if string is present
 143                          */
 144                         rp->oldline = (wchar_t *) wsdup(line);
 145                         if ( dollar != NULL ){
 146                                 rp->cond_macrostring = 
 147                                     (wchar_t *) wsdup(dollar - VER_LEN + 1);
 148                         }
 149                         /* 
 150                          * get target name into recursive struct
 151                          */
 152                         *colon = (int) nul_char;
 153                         rp->target = (wchar_t *) wsdup(line);
 154                         *bpatch = rp;
 155                         bpatch = &rp->next;
 156                 }
 157         }
 158         (void) fclose(fp);
 159 }
 160 
 161 /*
 162  *      report_recursive_dep(target, line)
 163  *
 164  *      Report a target as recursive.
 165  *
 166  *      Parameters:
 167  *              line            Dependency line reported
 168  *
 169  *      Static variables used:
 170  *              bpatch          Points to slot where next cell should be added
 171  *              changed         Written if report set changed
 172  */
 173 void
 174 report_recursive_dep(Name target, wchar_t *line)
 175 {
 176         Recursive_make  rp;
 177         wchar_t         rec_buf[STRING_BUFFER_LENGTH];
 178         String_rec      string;
 179 
 180         INIT_STRING_FROM_STACK(string, rec_buf);
 181         cond_macros_into_string(target, &string);
 182         /* 
 183          * find an applicable recursive entry, if there isn't one, create it
 184          */
 185         rp = find_recursive_target(target);
 186         if (rp == NULL) {
 187                 rp = ALLOC(Recursive_make);
 188                 (void) memset((char *) rp, 0, sizeof (Recursive_make_rec));
 189                 wchar_t * wcb = get_wstring(target->string_mb); // XXX Tolik: needs retmem
 190                 rp->target = wcb;
 191                 rp->newline = (wchar_t *) wsdup(line);
 192                 rp->cond_macrostring = (wchar_t *) wsdup(rec_buf);
 193                 *bpatch = rp;
 194                 bpatch = &rp->next;
 195                 changed = true;
 196         } else {
 197                 if ((rp->oldline != NULL) && !IS_WEQUAL(rp->oldline, line)) {
 198                         rp->newline = (wchar_t *) wsdup(line);
 199                         changed = true;
 200                 }
 201                 rp->removed = false;
 202         }
 203 }
 204 
 205 /*
 206  *      find_recursive_target(target)
 207  *
 208  *      Search the list for a given target.
 209  *
 210  *      Return value:
 211  *                              The target cell
 212  *
 213  *      Parameters:
 214  *              target          The target we need
 215  *              top_level_target more info used to determinde the 
 216  *                               target we need
 217  *
 218  *      Static variables used:
 219  *              recursive_list  The list of targets
 220  */
 221 Recursive_make
 222 find_recursive_target(Name target)
 223 {
 224         Recursive_make  rp;
 225         String_rec      string;
 226         wchar_t         rec_buf[STRING_BUFFER_LENGTH]; 
 227 
 228         INIT_STRING_FROM_STACK(string, rec_buf);
 229         cond_macros_into_string(target, &string);
 230 
 231         Wstring tstr(target);
 232         wchar_t * wcb = tstr.get_string();
 233         for (rp = recursive_list; rp != NULL; rp = rp->next) {
 234                 /* 
 235                  * If this entry has already been removed, ignore it.
 236                  */
 237                 if (rp->removed)
 238                         continue;
 239                 /* 
 240                  * If this target, and the target on the list are the same
 241                  * and if one of them contains conditional macro info, while
 242                  * the other doesn't,  remove this entry from the list of
 243                  * recursive entries.  This can only happen if the Makefile
 244                  * has changed to no longer contain conditional macros.
 245                  */
 246                 if (IS_WEQUAL(rp->target, wcb)) {
 247                         if (rp->cond_macrostring[VER_LEN] == '\0' &&
 248                             string.buffer.start[VER_LEN] != '\0'){
 249                                 rp->removed = true;
 250                                 continue;
 251                         } else if (rp->cond_macrostring[VER_LEN] != '\0' &&
 252                             string.buffer.start[VER_LEN] == '\0'){
 253                                 rp->removed = true;
 254                                 continue;
 255                         } 
 256                 }
 257                 /*
 258                  * If this is not a VERS2 entry,  only need to match
 259                  * the target name.  toptarg information from VERS1 entries
 260                  * are ignored.
 261                  */
 262                 MBSTOWCS(wcs_buffer, DEPINFO_FMT_VERSION);
 263                 if (IS_WEQUALN(wcs_buffer, string.buffer.start, VER_LEN)) { 
 264                         if (IS_WEQUAL(rp->cond_macrostring, 
 265                             string.buffer.start) &&
 266                             IS_WEQUAL(rp->target, wcb)) {
 267                                 return rp;
 268                         }
 269                 } else {
 270                         if (IS_WEQUAL(rp->target, wcb)) {
 271                                 return rp;
 272                         }
 273                 }
 274         }
 275         return NULL;
 276 }
 277 
 278 /*
 279  *      remove_recursive_dep(target, top_level_target)
 280  *
 281  *      Mark a target as no longer recursive.
 282  *
 283  *      Parameters:
 284  *              target          The target we want to remove
 285  *              top_level_target target we want to remove must be built from 
 286  *                               the same top level target
 287  *
 288  *      Static variables used:
 289  *              changed         Written if report set changed
 290  */
 291 void
 292 remove_recursive_dep(Name target)
 293 {
 294         Recursive_make  rp;
 295 
 296         rp = find_recursive_target(target);
 297 
 298         if ( rp != NULL ) {
 299                 rp->removed = true;  
 300                 changed = true;
 301                 if(rp->target) {
 302                         retmem(rp->target);
 303                         rp->target = NULL;
 304                 }
 305                 if(rp->newline) {
 306                         retmem(rp->newline);
 307                         rp->newline = NULL;
 308                 }
 309                 if(rp->oldline) {
 310                         retmem(rp->oldline);
 311                         rp->oldline = NULL;
 312                 }
 313                 if(rp->cond_macrostring) {
 314                         retmem(rp->cond_macrostring);
 315                         rp->cond_macrostring = NULL;
 316                 }
 317         }
 318 }
 319 
 320 #ifdef NSE
 321 /*
 322  *      report_recursive_done()
 323  *
 324  *      Write the .nse_depinfo file.
 325  *
 326  *      Parameters:
 327  *
 328  *      Static variables used:
 329  *              recursive_list  The list of targets
 330  *              changed         Written if report set changed
 331  *
 332  *      Global variables used:
 333  *              recursive_name  The Name ".RECURSIVE", compared against
 334  */
 335 void
 336 report_recursive_done(void)
 337 {
 338         char            *search_dir;
 339         char            nse_depinfo[MAXPATHLEN];
 340         char            tmpfile[MAXPATHLEN];
 341         FILE            *ofp;
 342         FILE            *ifp;
 343         wchar_t         *space;
 344         wchar_t         *data;
 345         wchar_t         *line;
 346         wchar_t         *bigger_line;
 347         int             line_size, line_index;
 348         int             lock_err;
 349         Recursive_make  rp;
 350 
 351         if (changed == false) {
 352                 return;
 353         }
 354 
 355         search_dir = getenv(NOCATGETS("NSE_DEP"));
 356         if (search_dir == NULL) {
 357                 return;
 358         }
 359         (void) sprintf(nse_depinfo, "%s/%s", search_dir, NSE_DEPINFO);
 360         (void) sprintf(tmpfile, "%s.%d", nse_depinfo, getpid());
 361         ofp = fopen(tmpfile, "w");
 362         if (ofp == NULL) {
 363                 (void) fprintf(stderr,
 364                                catgets(catd, 1, 116, "Cannot open `%s' for writing\n"),
 365                                tmpfile);
 366                 return;
 367         }
 368         (void) sprintf(nse_depinfo_lockfile, 
 369                        "%s/%s", search_dir, NSE_DEPINFO_LOCK);
 370         if (lock_err = file_lock(nse_depinfo, 
 371                                  nse_depinfo_lockfile, 
 372                                  (int *) &nse_depinfo_locked, 0)) {
 373                 (void) fprintf(stderr,
 374                                catgets(catd, 1, 117, "writing .RECURSIVE lines to %s\n"),
 375                                tmpfile);
 376                 (void) fprintf(stderr,
 377                                catgets(catd, 1, 118, "To recover, merge .nse_depinfo.%d with .nse_depinfo\n"),
 378                                getpid(),
 379                                catgets(catd, 1, 119, "with .nse_depinfo"));
 380         }
 381 
 382         if (nse_depinfo_locked) {
 383                 ifp = fopen(nse_depinfo, "r");
 384                 if (ifp != NULL) {
 385                         /*
 386                          * Copy all the non-.RECURSIVE lines from 
 387                          * the old file to the new one.
 388                          */
 389                         line_size = MAXPATHLEN;
 390                         line_index = line_size - 1;
 391                         line = ALLOC_WC(line_size);
 392                         while (fgetws(line, line_size, ifp) != NULL) {
 393                                 while (wslen(line) == line_index) {
 394                                         if (line[wslen(line) - 1] == '\n') {
 395                                                 continue;
 396                                         }
 397                                         bigger_line = ALLOC_WC(2 * line_size);
 398                                         wscpy(bigger_line, line);
 399                                         retmem(line);
 400                                         line = bigger_line;
 401                                         if (fgetws(&line[line_index], 
 402                                                   line_size, ifp) == NULL)
 403                                           continue;
 404                                         line_index = 2 * line_index;
 405                                         line_size = 2 * line_size;
 406                                 }
 407 
 408                                 space = wschr(line, (int) space_char);
 409                                 if (space != NULL && 
 410                                     IS_WEQUALN(&space[1],
 411                                               recursive_name->string,
 412                                               (int) recursive_name->hash.length)) {
 413                                         continue;
 414                                 }
 415                                 WCSTOMBS(mbs_buffer, line);
 416                                 (void) fprintf(ofp, "%s", mbs_buffer);
 417                         }
 418                         (void) fclose(ifp);
 419                 }
 420         }
 421 
 422         /*
 423          * Write out the .RECURSIVE lines.
 424          */
 425         for (rp = recursive_list; rp != NULL; rp = rp->next) {
 426                 if (rp->removed) {
 427                         continue;
 428                 }
 429                 if (rp->newline != NULL) {
 430                         data = rp->newline;
 431                 } else {
 432                         data = rp->oldline;
 433                 }
 434                 if (data != NULL) {
 435                         WCSTOMBS(mbs_buffer, data);
 436                         (void) fprintf(ofp, "%s\n", mbs_buffer);
 437                 }
 438         }
 439         (void) fclose(ofp);
 440 
 441         if (nse_depinfo_locked) {
 442                 (void) rename(tmpfile, nse_depinfo);
 443                 (void) unlink(nse_depinfo_lockfile);
 444                 nse_depinfo_locked = false;
 445                 nse_depinfo_lockfile[0] = '\0';
 446                 (void) chmod(nse_depinfo, 0666);
 447         }
 448 }
 449 #endif // NSE
 450 
 451 /* gather_recursive_deps()
 452  *
 453  *      Create or update list of recursive targets.  
 454  */
 455 void
 456 gather_recursive_deps(void)
 457 {
 458         Name_set::iterator      np, e;
 459         String_rec              rec;
 460         wchar_t                 rec_buf[STRING_BUFFER_LENGTH];
 461         register Property       lines;
 462         Boolean                 has_recursive;
 463         Dependency              dp;
 464 
 465         report_recursive_init();
 466 
 467         /* Go thru all targets and dump recursive dependencies */
 468         for (np = hashtab.begin(), e = hashtab.end(); np != e; np++) {
 469                 if (np->has_recursive_dependency){
 470                         has_recursive = false;
 471                         /* 
 472                          * start .RECURSIVE line with target:
 473                          */
 474                         INIT_STRING_FROM_STACK(rec, rec_buf);
 475                         APPEND_NAME(np, &rec, FIND_LENGTH);
 476                         append_char((int) colon_char, &rec);
 477                         append_char((int) space_char, &rec);
 478                         
 479                         for (lines = get_prop(np->prop,recursive_prop); 
 480                             lines != NULL;
 481                             lines = get_prop(lines->next, recursive_prop)) {
 482                                 /* 
 483                                  * if entry is already in depinfo
 484                                  * file or entry was not built, ignore it
 485                                  */
 486                                 if (lines->body.recursive.in_depinfo)
 487                                         continue;
 488                                 if (!lines->body.recursive.has_built)
 489                                         continue;
 490                                 has_recursive = true;
 491                                 lines->body.recursive.in_depinfo=true;
 492                                 
 493                                 /* 
 494                                 * Write the remainder of the
 495                                 * .RECURSIVE line 
 496                                 */
 497                                 APPEND_NAME(recursive_name, &rec, 
 498                                     FIND_LENGTH);
 499                                 append_char((int) space_char, &rec);
 500                                 APPEND_NAME(lines->body.recursive.directory,
 501                                         &rec, FIND_LENGTH);
 502                                 append_char((int) space_char, &rec);
 503                                 APPEND_NAME(lines->body.recursive.target,
 504                                         &rec, FIND_LENGTH);
 505                                 append_char((int) space_char, &rec);
 506                                 
 507                                 /* Complete list of makefiles used */
 508                                 for (dp = lines->body.recursive.makefiles; 
 509                                     dp != NULL; 
 510                                     dp = dp->next) {
 511                                         APPEND_NAME(dp->name, &rec,  FIND_LENGTH);
 512                                         append_char((int) space_char, &rec);
 513                                 }
 514                         }  
 515                         /* 
 516                          * dump list of conditional targets,
 517                          * and report recursive entry, if needed
 518                          */
 519                         cond_macros_into_string(np, &rec);
 520                         if (has_recursive){
 521                                 report_recursive_dep(np, rec.buffer.start);
 522                         }
 523 
 524                 } else if ( np->has_built ) {
 525                         remove_recursive_dep(np);
 526                 }
 527         }
 528 }
 529