usr.sbin/makefs: Sync with sys/vfs/hammer2
[dragonfly.git] / usr.bin / dsynth / dsynth.c
blob1c63a3c3bdde46cfdb4a216d88c5ba6e7c21aee0
1 /*
2 * Copyright (c) 2019 The DragonFly Project. All rights reserved.
4 * This code is derived from software contributed to The DragonFly Project
5 * by Matthew Dillon <dillon@backplane.com>
7 * This code uses concepts and configuration based on 'synth', by
8 * John R. Marino <draco@marino.st>, which was written in ada.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in
18 * the documentation and/or other materials provided with the
19 * distribution.
20 * 3. Neither the name of The DragonFly Project nor the names of its
21 * contributors may be used to endorse or promote products derived
22 * from this software without specific, prior written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
27 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
28 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
29 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
30 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
31 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
32 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
33 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
34 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * SUCH DAMAGE.
38 #include "dsynth.h"
40 static int CheckAddReExec(int lkfd);
41 static void DoAddReExec(int lkfd, int ac, char **oldav);
42 static void DoInit(void);
43 static void usage(int ecode) __dead2;
45 int ForceOpt;
46 int OverridePkgDeleteOpt;
47 int FetchOnlyOpt;
48 int YesOpt;
49 int DebugOpt;
50 int MaskProbeAbort;
51 int ColorOpt = 1;
52 int NullStdinOpt = 1;
53 int SlowStartOpt = -1;
54 int CapabilityRestrictions;
55 long PkgDepMemoryTarget;
56 long PkgDepScaleTarget = 100; /* 1.00 */
57 char *DSynthExecPath;
58 char *ProfileOverrideOpt;
59 int NiceOpt = 10;
61 int
62 main(int ac, char **av)
64 char *lkpath;
65 pkg_t *pkgs;
66 int lkfd;
67 int isworker;
68 int c;
69 int sopt;
70 int doadds;
72 #if defined(__DragonFly__)
74 * The system is expected to have capabilities
77 size_t len = sizeof(CapabilityRestrictions);
78 sysctlbyname("kern.caps_available",
79 &CapabilityRestrictions, &len, NULL, 0);
80 if (CapabilityRestrictions == 0)
81 fprintf(stderr, "caps restrictions unavailable\n");
83 #endif
86 * Get our exec path so we can self-exec clean WORKER
87 * processes.
90 size_t len;
91 const int name[] = {
92 CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1,
94 if (sysctl(name, 4, NULL, &len, NULL, 0) < 0)
95 dfatal_errno("Cannot get binary path");
96 DSynthExecPath = malloc(len + 1);
97 if (sysctl(name, 4, DSynthExecPath, &len, NULL, 0) < 0)
98 dfatal_errno("Cannot get binary path");
99 DSynthExecPath[len] = 0;
103 * Override profile in dsynth.ini (can be further overridden
104 * with the -p profile option).
106 ProfileOverrideOpt = getenv("DSYNTH_PROFILE");
109 * Process options and make sure the directive is present
111 sopt = 0;
112 while ((c = getopt(ac, av, "dfhm:p:vxys:DPM:NS")) != -1) {
113 switch(c) {
114 case 'f':
115 ++ForceOpt;
116 break;
117 case 'x':
118 ++OverridePkgDeleteOpt;
119 break;
120 case 'y':
121 ++YesOpt;
122 break;
123 case 'D':
124 WorkerProcFlags |= WORKER_PROC_DEVELOPER;
125 break;
126 case 'P':
127 WorkerProcFlags |= WORKER_PROC_CHECK_PLIST;
128 break;
129 case 'S':
130 UseNCurses = 0;
131 if (++sopt == 2)
132 ColorOpt = 0;
133 break;
134 case 'N':
135 NiceOpt = 0;
136 break;
137 case 'd':
138 ++DebugOpt;
139 if (DebugOpt >= 2)
140 UseNCurses = 0;
141 break;
142 case 'h':
143 usage(0);
144 /* NOT REACHED */
145 exit(0);
146 case 'v':
147 printf("dsynth %s\n", DSYNTH_VERSION);
148 exit(0);
149 case 's':
151 * Start with N jobs, increasing to the configured
152 * maximum slowly. 0 to disable (starts with the
153 * full count).
155 SlowStartOpt = strtol(optarg, NULL, 0);
156 break;
157 case 'm':
158 PkgDepMemoryTarget = strtoul(optarg, NULL, 0);
159 PkgDepMemoryTarget *= ONEGB;
160 break;
161 case 'M':
162 PkgDepScaleTarget = strtod(optarg, NULL) * 100;
163 if (PkgDepScaleTarget < 1)
164 PkgDepScaleTarget = 1;
165 if (PkgDepScaleTarget > 9900)
166 PkgDepScaleTarget = 9900;
167 break;
168 case 'p':
169 ProfileOverrideOpt = optarg;
170 break;
171 default:
172 fprintf(stderr, "Unknown option: %c\n", c);
173 usage(2);
174 /* NOT REACHED */
175 break;
178 ac -= optind;
179 av += optind;
180 pkgs = NULL;
181 if (ac < 1) {
182 fprintf(stderr, "Missing directive\n");
183 usage(2);
184 /* NOT REACHED */
188 * Directives which do not require a working configuration
190 if (strcmp(av[0], "init") == 0) {
191 DoInit();
192 exit(0);
193 /* NOT REACHED */
195 if (strcmp(av[0], "help") == 0) {
196 usage(0);
197 exit(0);
198 /* NOT REACHED */
200 if (strcmp(av[0], "version") == 0) {
201 printf("dsynth %s\n", DSYNTH_VERSION);
202 exit(0);
203 /* NOT REACHED */
207 * Preconfiguration.
209 if (strcmp(av[0], "WORKER") == 0) {
210 isworker = 1;
211 } else {
212 isworker = 0;
215 signal(SIGPIPE, SIG_IGN);
216 ParseConfiguration(isworker);
219 * Lock file path (also contains any 'add' directives thrown in
220 * during a build).
222 asprintf(&lkpath, "%s/.lock", BuildBase);
225 * Setup some environment for bulk operations (pkglist scan).
226 * These are not used by the builder (the builder will replicate
227 * all of these).
229 * NOTE: PKG_SUFX - pkg versions older than 1.17
230 * PKG_COMPRESSION_FORMAT - pkg versions >= 1.17
232 addbuildenv("PORTSDIR", DPortsPath,
233 BENV_ENVIRONMENT | BENV_PKGLIST);
234 addbuildenv("BATCH", "yes",
235 BENV_ENVIRONMENT | BENV_PKGLIST);
236 addbuildenv("PKG_COMPRESSION_FORMAT", UsePkgSufx,
237 BENV_ENVIRONMENT | BENV_PKGLIST);
238 addbuildenv("PKG_SUFX", UsePkgSufx,
239 BENV_ENVIRONMENT | BENV_PKGLIST);
240 addbuildenv("PACKAGE_BUILDING", "yes",
241 BENV_ENVIRONMENT | BENV_PKGLIST);
242 addbuildenv("ARCH", ArchitectureName,
243 BENV_ENVIRONMENT | BENV_PKGLIST);
245 #if 0
249 addbuildenv("OSTYPE", OperatingSystemName,
250 BENV_ENVIRONMENT | BENV_PKGLIST);
251 addbuildenv("MACHTYPE", MachineName,
252 BENV_ENVIRONMENT | BENV_PKGLIST);
253 #endif
255 * SlowStart auto adjust. We nominally start with 1 job and increase
256 * it to the maximum every 5 seconds to give various dynamic management
257 * parameters time to stabilize.
259 * This can take a while on a many-core box with a high jobs setting,
260 * so increase the initial jobs in such cases.
262 if (SlowStartOpt > MaxWorkers)
263 SlowStartOpt = MaxWorkers;
264 if (SlowStartOpt < 0) {
265 if (MaxWorkers < 16)
266 SlowStartOpt = 1;
267 else
268 SlowStartOpt = MaxWorkers / 4;
272 * Special directive for when dsynth execs itself to manage
273 * a worker chroot.
275 if (isworker) {
276 WorkerProcess(ac, av);
277 exit(0);
281 * Build initialization and directive handling
283 DoInitBuild(-1);
286 * Directives that use the configuration but are not interlocked
287 * against a running dsynth.
289 if (strcmp(av[0], "monitor") == 0) {
290 char *spath;
291 char *lpath;
293 if (ac == 1) {
294 asprintf(&spath, "%s/%s", StatsBase, STATS_FILE);
295 asprintf(&lpath, "%s/%s", StatsBase, STATS_LOCKFILE);
296 MonitorDirective(spath, lpath);
297 free(spath);
298 free(lpath);
299 } else {
300 MonitorDirective(av[1], NULL);
302 exit(0);
303 /* NOT REACHED */
304 } else if (strcmp(av[0], "add") == 0) {
305 char *buf;
306 int fd;
307 int i;
310 * The lock check is a bit racey XXX
312 fd = open(lkpath, O_RDWR | O_CREAT | O_APPEND, 0644);
313 if (flock(fd, LOCK_EX | LOCK_NB) == 0) {
314 dfatal("No dsynth running to add ports to");
315 flock(fd, LOCK_UN);
317 for (i = 1; i < ac; ++i) {
318 asprintf(&buf, "%s\n", av[i]);
319 write(fd, buf, strlen(buf));
320 printf("added to run: %s\n", av[i]);
322 close(fd);
323 exit(0);
327 * Front-end exec (not a WORKER exec), normal startup. We have
328 * the configuration so the first thing we need to do is check
329 * the lock file.
331 lkfd = open(lkpath, O_RDWR | O_CREAT | O_CLOEXEC, 0644);
332 if (lkfd < 0)
333 dfatal_errno("Unable to create %s", lkpath);
334 if (flock(lkfd, LOCK_EX | LOCK_NB) < 0) {
335 dfatal("Another dsynth is using %s, exiting",
336 BuildBase);
340 * Starting a new run cleans out any prior add directives
341 * that may have been pending.
343 ftruncate(lkfd, 0);
344 /* leave descriptor open */
346 doadds = 0;
348 if (strcmp(av[0], "debug") == 0) {
349 DoCleanBuild(1);
350 OptimizeEnv();
351 pkgs = ParsePackageList(ac - 1, av + 1, 1);
352 RemovePackages(pkgs);
353 DoBuild(pkgs);
354 doadds = 1;
355 } else if (strcmp(av[0], "status") == 0) {
356 OptimizeEnv();
357 if (ac - 1)
358 pkgs = ParsePackageList(ac - 1, av + 1, 0);
359 else
360 pkgs = GetLocalPackageList();
361 DoStatus(pkgs);
362 } else if (strcmp(av[0], "cleanup") == 0) {
363 DoCleanBuild(0);
364 } else if (strcmp(av[0], "configure") == 0) {
365 DoCleanBuild(0);
366 DoConfigure();
367 } else if (strcmp(av[0], "fetch-only") == 0) {
368 if (SlowStartOpt == -1)
369 SlowStartOpt = 999;
370 if (PkgDepScaleTarget == 100)
371 PkgDepScaleTarget = 999;
372 ++FetchOnlyOpt;
373 ++YesOpt;
374 WorkerProcFlags |= WORKER_PROC_FETCHONLY;
375 DoCleanBuild(1);
376 OptimizeEnv();
377 if (ac == 2 && strcmp(av[1], "everything") == 0) {
378 MaskProbeAbort = 1;
379 pkgs = GetFullPackageList();
380 } else {
381 pkgs = ParsePackageList(ac - 1, av + 1, 0);
383 DoBuild(pkgs);
384 doadds = 1;
385 } else if (strcmp(av[0], "list-system") == 0) {
386 FILE *fp;
388 DoCleanBuild(1);
389 OptimizeEnv();
390 pkgs = GetLocalPackageList();
391 if ((fp = fopen("build.txt", "w")) != NULL) {
392 while (pkgs) {
393 fprintf(fp, "%s\n", pkgs->portdir);
394 pkgs = pkgs->bnext;
396 fclose(fp);
397 printf("list written to build.txt\n");
398 } else {
399 fprintf(stderr, "Cannot create 'build.txt'\n");
400 exit(1);
402 } else if (strcmp(av[0], "upgrade-system") == 0) {
403 DoCleanBuild(1);
404 OptimizeEnv();
405 pkgs = GetLocalPackageList();
406 DoBuild(pkgs);
407 DoRebuildRepo(0);
408 DoUpgradePkgs(pkgs, 0);
409 dfatal("NOTE: you have to pkg upgrade manually");
410 } else if (strcmp(av[0], "prepare-system") == 0) {
411 DeleteObsoletePkgs = 1;
412 DoCleanBuild(1);
413 OptimizeEnv();
414 pkgs = GetLocalPackageList();
415 DoBuild(pkgs);
416 DoRebuildRepo(0);
417 } else if (strcmp(av[0], "rebuild-repository") == 0) {
418 OptimizeEnv();
419 DoRebuildRepo(0);
420 } else if (strcmp(av[0], "purge-distfiles") == 0) {
421 OptimizeEnv();
422 pkgs = GetFullPackageList();
423 PurgeDistfiles(pkgs);
424 } else if (strcmp(av[0], "reset-db") == 0) {
425 char *dbmpath;
427 asprintf(&dbmpath, "%s/ports_crc.db", BuildBase);
428 remove(dbmpath);
429 printf("%s reset, will be regenerated on next build\n",
430 dbmpath);
431 free(dbmpath);
432 } else if (strcmp(av[0], "status-everything") == 0) {
433 OptimizeEnv();
434 pkgs = GetFullPackageList();
435 DoStatus(pkgs);
436 } else if (strcmp(av[0], "everything") == 0) {
437 if (WorkerProcFlags & WORKER_PROC_DEVELOPER)
438 WorkerProcFlags |= WORKER_PROC_CHECK_PLIST;
439 MaskProbeAbort = 1;
440 DeleteObsoletePkgs = 1;
441 DoCleanBuild(1);
442 OptimizeEnv();
443 pkgs = GetFullPackageList();
444 DoBuild(pkgs);
445 DoRebuildRepo(!CheckAddReExec(lkfd));
446 } else if (strcmp(av[0], "build") == 0) {
447 DoCleanBuild(1);
448 OptimizeEnv();
449 pkgs = ParsePackageList(ac - 1, av + 1, 0);
450 DoBuild(pkgs);
451 DoRebuildRepo(!CheckAddReExec(lkfd));
452 DoUpgradePkgs(pkgs, 1);
453 doadds = 1;
454 } else if (strcmp(av[0], "just-build") == 0) {
455 DoCleanBuild(1);
456 OptimizeEnv();
457 pkgs = ParsePackageList(ac - 1, av + 1, 0);
458 DoBuild(pkgs);
459 doadds = 1;
460 } else if (strcmp(av[0], "install") == 0) {
461 DoCleanBuild(1);
462 OptimizeEnv();
463 pkgs = ParsePackageList(ac - 1, av + 1, 0);
464 DoBuild(pkgs);
465 DoRebuildRepo(0);
466 DoUpgradePkgs(pkgs, 0);
467 doadds = 1;
468 } else if (strcmp(av[0], "force") == 0) {
469 DoCleanBuild(1);
470 OptimizeEnv();
471 pkgs = ParsePackageList(ac - 1, av + 1, 0);
472 RemovePackages(pkgs);
473 DoBuild(pkgs);
474 DoRebuildRepo(!CheckAddReExec(lkfd));
475 DoUpgradePkgs(pkgs, 1);
476 doadds = 1;
477 } else if (strcmp(av[0], "test") == 0) {
478 WorkerProcFlags |= WORKER_PROC_CHECK_PLIST |
479 WORKER_PROC_INSTALL |
480 WORKER_PROC_DEINSTALL;
481 DoCleanBuild(1);
482 OptimizeEnv();
483 pkgs = ParsePackageList(ac - 1, av + 1, 0);
484 RemovePackages(pkgs);
485 WorkerProcFlags |= WORKER_PROC_DEVELOPER;
486 DoBuild(pkgs);
487 doadds = 1;
488 } else {
489 fprintf(stderr, "Unknown directive '%s'\n", av[0]);
490 usage(2);
494 * For directives that support the 'add' directive, check for
495 * additions and re-exec.
497 * Note that the lockfile is O_CLOEXEC and will be remade on exec.
499 * XXX a bit racey vs adds done just as we are finishing
501 if (doadds && CheckAddReExec(lkfd))
502 DoAddReExec(lkfd, optind + 1, av - optind);
504 return 0;
508 * If the 'add' directive was issued while a dsynth build was in
509 * progress, we re-exec dsynth with its original options and
510 * directive along with the added ports.
512 static int
513 CheckAddReExec(int lkfd)
515 struct stat st;
517 if (fstat(lkfd, &st) < 0 || st.st_size == 0)
518 return 0;
519 return 1;
522 static void
523 DoAddReExec(int lkfd, int ac, char **oldav)
525 struct stat st;
526 char *buf;
527 char **av;
528 size_t bi;
529 size_t i;
530 int nadd;
531 int n;
533 if (fstat(lkfd, &st) < 0 || st.st_size == 0)
534 return;
535 buf = malloc(st.st_size + 1);
536 if (read(lkfd, buf, st.st_size) != st.st_size) {
537 free(buf);
538 return;
540 buf[st.st_size] = 0;
542 nadd = 0;
543 for (i = 0; i < (size_t)st.st_size; ++i) {
544 if (buf[i] == '\n' || buf[i] == 0) {
545 buf[i] = 0;
546 ++nadd;
550 av = calloc(ac + nadd + 1, sizeof(char *));
552 for (n = 0; n < ac; ++n)
553 av[n] = oldav[n];
555 nadd = 0;
556 bi = 0;
557 for (i = 0; i < (size_t)st.st_size; ++i) {
558 if (buf[i] == 0) {
559 av[ac + nadd] = buf + bi;
560 bi = i + 1;
561 ++nadd;
565 printf("dsynth re-exec'ing additionally added packages\n");
566 for (n = 0; n < ac + nadd; ++n)
567 printf(" %s", av[n]);
568 printf("\n");
569 fflush(stdout);
570 sleep(2);
571 execv(DSynthExecPath, av);
574 static void
575 DoInit(void)
577 struct stat st;
578 char *path;
579 FILE *fp;
581 if (stat(ConfigBase1, &st) == 0) {
582 dfatal("init will not overwrite %s", ConfigBase1);
584 if (stat(ConfigBase2, &st) == 0) {
585 dfatal("init will not create %s if %s exists",
586 ConfigBase2, ConfigBase1);
588 if (mkdir(ConfigBase1, 0755) < 0)
589 dfatal_errno("Unable to mkdir %s", ConfigBase1);
591 asprintf(&path, "%s/dsynth.ini", ConfigBase1);
592 fp = fopen(path, "w");
593 dassert_errno(fp, "Unable to create %s", path);
594 fprintf(fp, "%s",
595 "; This Synth configuration file is automatically generated\n"
596 "; Take care when hand editing!\n"
597 "\n"
598 "[Global Configuration]\n"
599 "profile_selected= LiveSystem\n"
600 "\n"
601 "[LiveSystem]\n"
602 "Operating_system= DragonFly\n"
603 "Directory_packages= /build/synth/live_packages\n"
604 "Directory_repository= /build/synth/live_packages/All\n"
605 "Directory_portsdir= /build/synth/dports\n"
606 "Directory_options= /build/synth/options\n"
607 "Directory_distfiles= /build/synth/distfiles\n"
608 "Directory_buildbase= /build/synth/build\n"
609 "Directory_logs= /build/synth/logs\n"
610 "Directory_ccache= disabled\n"
611 "Directory_system= /\n"
612 "Package_suffix= .txz\n"
613 "Number_of_builders= 0\n"
614 "Max_jobs_per_builder= 0\n"
615 "Tmpfs_workdir= true\n"
616 "Tmpfs_localbase= true\n"
617 "Display_with_ncurses= true\n"
618 "leverage_prebuilt= false\n"
619 "; Meta_version= 2\n"
620 "; Check_plist= false\n"
621 "; Numa_setsize= 2\n"
622 "\n");
623 if (fclose(fp))
624 dfatal_errno("Unable to write to %s\n", ConfigBase1);
625 free(path);
627 asprintf(&path, "%s/LiveSystem-make.conf", ConfigBase1);
628 fp = fopen(path, "w");
629 dassert_errno(fp, "Unable to create %s", path);
630 fprintf(fp, "%s",
631 "#\n"
632 "# Various dports options that might be of interest\n"
633 "#\n"
634 "#LICENSES_ACCEPTED= NONE\n"
635 "#DISABLE_LICENSES= yes\n"
636 "#DEFAULT_VERSIONS= ssl=openssl\n"
637 "#FORCE_PACKAGE= yes\n"
638 "#DPORTS_BUILDER= yes\n"
639 "#\n"
640 "# Turn these on to generate debug binaries. However, these\n"
641 "# options will seriously bloat memory use and storage use,\n"
642 "# do not use lightly\n"
643 "#\n"
644 "#STRIP=\n"
645 "#WITH_DEBUG=yes\n"
647 if (fclose(fp))
648 dfatal_errno("Unable to write to %s\n", ConfigBase1);
649 free(path);
652 __dead2 static void
653 usage(int ecode)
655 if (ecode == 2) {
656 fprintf(stderr, "Run 'dsynth help' for usage\n");
657 exit(1);
660 fprintf(stderr,
661 "dsynth [options] directive\n"
662 " -d - Debug verbosity (-dd disables ncurses)\n"
663 " -f - Force (for purge-distfiles)\n"
664 " -h - Display this screen and exit\n"
665 " -m gb - Load management based on pkgdep memory\n"
666 " -p profile - Override profile selected in dsynth.ini\n"
667 " -s n - Set initial DynamicMaxWorkers\n"
668 " -v - Print version info and exit\n"
669 " -x - Do not rebuild packages with dependencies\n"
670 " which require rebuilding\n"
671 " -xx - Do not rebuild packages whos dports trees\n"
672 " change\n"
673 " -y - Automatically answer yes to dsynth questions\n"
674 " -D - Enable DEVELOPER mode\n"
675 " -P - Include the check-plist stage\n"
676 " -S - Disable ncurses\n"
677 " -N - Do not nice-up sub-processes (else nice +10)\n"
678 "\n"
679 " init - Initialize /etc/dsynth\n"
680 " status - Dry-run of 'upgrade-system'\n"
681 " cleanup - Clean-up mounts\n"
682 " configure - Bring up configuration menu\n"
683 " list-system - Just generate the build list to build.txt\n"
684 " upgrade-system - Incremental build and upgrade using pkg list\n"
685 " from local system, then upgrade the local\n"
686 " system.\n"
687 " prepare-system - 'upgrade-system' but stops after building\n"
688 " rebuild-repository - Rebuild database files for current repository\n"
689 " purge-distfiles - Delete obsolete source distribution files\n"
690 " reset-db - Delete ports_crc.db, regenerate next build\n"
691 " status-everything - Dry-run of 'everything'\n"
692 " everything - Build entire dports tree and repo database\n"
693 " (-D everything infers -P)\n"
694 " version - Print version info and exit\n"
695 " help - Display this screen and exit\n"
696 " status [ports] - Dry-run of 'build' with given list\n"
697 " build [ports] - Incrementally build dports based on the given\n"
698 " list, but asks before updating the repo\n"
699 " database and system\n"
700 " just-build [ports] - 'build' but skips post-build steps\n"
701 " install [ports] - 'build' but upgrades system without asking\n"
702 " force [ports] - 'build' but deletes existing packages first\n"
703 " test [ports] - 'build' w/DEVELOPER=yes and pre-deletes pkgs\n"
704 " (also infers -P)\n"
705 " debug [ports] - like 'test' but leaves mounts intact\n"
706 " fetch-only [ports] - Fetch src dists only ('everything' ok)\n"
707 " monitor [datfile] - Monitor a running dsynth\n"
708 "\n"
709 " [ports] is a space-delimited list of origins, e.g. editors/joe. It\n"
710 " may also be a path to a file containing one origin per line.\n"
713 exit(ecode);