makefile: make bc and cs rules more symmetic

Currently, `make` is the same as `make bc`, but the idea is that
`make` can become the same as `make cs` when some other pieces are in
place.

The source of the top-level makefile is now ".makefile", and it's
turned into "Makefile" using "racket/src/makemake.rkt". The
transformation makes non-GNU `make` variants and `nmake` act like GNU
make to propagate variables, which makes abstraction through targets
plus variables (admitedly an abuse of `make`) more reliable and
consistent.

Why abuse `make` this way? Because `make` variants and `nmake` are
similar enough that to constitute a portable scripting language, and
one that conveniently provides a large number of entry points.
This commit is contained in:
Matthew Flatt 2020-07-29 20:27:50 -06:00
parent a4b6ac701e
commit 1d9db6eec3
13 changed files with 1873 additions and 904 deletions

1131
.makefile Normal file

File diff suppressed because it is too large Load Diff

1268
Makefile

File diff suppressed because it is too large Load Diff

View File

@ -116,9 +116,9 @@ On Unix (including Linux) and Mac OS, `make` (or `make in-place`)
creates a build in the `"racket"` directory.
On Windows with Microsoft Visual Studio (any version between 2008/9.0
and 2019/16.0), `nmake win32-in-place` creates a build in the `"racket"`
directory. For information on configuring your command-line environment
for Visual Studio, see `"racket/src/worksp/README.txt"`.
and 2019/16.0), `nmake win` creates a build in the `"racket"` directory.
For information on configuring your command-line environment for Visual
Studio, see `"racket/src/worksp/README.txt"`.
On Windows with MinGW, use `make PLAIN_RACKET=racket/racket`, since
MinGW uses Unix-style tools but generates a Windows-layout Racket build.
@ -166,10 +166,7 @@ read `"racket/src/README"` for more information.
If you would like to provide arguments to `configure` for the minimal
Racket build, then you can supply them with by adding
`CONFIGURE_ARGS_qq="<options>"` to `make in-place` or `make unix-style`.
(The `_qq` suffix on the variable name `CONFIGURE_ARGS_qq` is a
convention that indicates that single- and double-quote marks are
allowed in the value.)
`CONFIGURE_ARGS="<options>"` to `make in-place` or `make unix-style`.
The `"pkgs"` directory contains packages that are tied to the Racket
core implementation and are therefore kept in the same Git repository. A
@ -213,7 +210,7 @@ and the `raco setup` part, use
which recurs with `make -j <n> JOB_OPTIONS="-j <n>"`. Setting `CPUS`
also works with `make unix-style`.
Use `make as-is` (or `nmake win32-as-is`) to perform the same build
Use `make as-is` (or `nmake win-as-is`) to perform the same build
actions as `make in-place`, but without consulting any package catalogs
or package sources to install or update packages. In other words, use
`make as-is` to rebuild after local changes that could include changes
@ -228,7 +225,7 @@ below.
### 1.6. More Instructions: Building Racket on Chez Scheme
The `make cs` target (or `make cs-as-is` for a rebuild, or `nmake
win32-cs` on Windows with Visual Studio) builds a variant of Racket that
win-cs` on Windows with Visual Studio) builds a variant of Racket that
runs on Chez Scheme. By default, the executables for the Racket CS
variant all have a `cs` or `CS` suffix, and they coexist with a Racket
BC build by keeping compiled files in a machine-specific subdirectory of
@ -261,8 +258,8 @@ and follow the `"README.txt"` there, which gives you more configuration
options.
If you dont want any special configuration and you just want the base
build, you can use `make base` (or `nmake win32-base`) with the
top-level makefile.
build, you can use `make base` (or `nmake win-base`) with the top-level
makefile.
Minimal Racket does not require additional native libraries to run, but
under Windows, encoding-conversion, extflonum, and SSL functionality is
@ -276,7 +273,7 @@ libraries. See the documentation for `raco setup` for information on the
options.
For cross compilation, add configuration options to
`CONFIGURE_ARGS_qq="<options>"` as described in the `"README.txt"` of
`CONFIGURE_ARGS="<options>"` as described in the `"README.txt"` of
`"racket/src"`, but also add a `PLAIN_RACKET=...` argument for the
top-level makefile to specify the same executable as in an
`--enable-racket=...` for `configure`. In general, the `PLAIN_RACKET`

View File

@ -87,7 +87,7 @@ On Unix (including Linux) and Mac OS, @exec{make} (or @exec{make in-place})
creates a build in the @filepath{racket} directory.
On Windows with Microsoft Visual Studio (any version between 2008/9.0
and 2019/16.0), @exec{nmake win32-in-place} creates a build in the
and 2019/16.0), @exec{nmake win} creates a build in the
@filepath{racket} directory. For information on configuring your
command-line environment for Visual Studio, see
@filepath{racket/src/worksp/README.txt}.
@ -144,10 +144,8 @@ information.
If you would like to provide arguments to @exec{configure} for the
minimal Racket build, then you can supply them with by adding
@exec{CONFIGURE_ARGS_qq="@nonterm{options}"} to @exec{make in-place}
or @exec{make unix-style}. (The @tt{_qq} suffix on the variable name
@tt{CONFIGURE_ARGS_qq} is a convention that indicates that single- and
double-quote marks are allowed in the value.)
@exec{CONFIGURE_ARGS="@nonterm{options}"} to @exec{make in-place}
or @exec{make unix-style}.
The @filepath{pkgs} directory contains packages that are tied to the
Racket core implementation and are therefore kept in the same Git
@ -193,7 +191,7 @@ which recurs with @exec{make -j @nonterm{n} JOB_OPTIONS="-j
@nonterm{n}"}. Setting @exec{CPUS} also works with @exec{make
unix-style}.
Use @exec{make as-is} (or @exec{nmake win32-as-is}) to perform the
Use @exec{make as-is} (or @exec{nmake win-as-is}) to perform the
same build actions as @exec{make in-place}, but without consulting any
package catalogs or package sources to install or update packages. In
other words, use @exec{make as-is} to rebuild after local changes that
@ -208,7 +206,7 @@ If you need even more control over the build, carry on to
@section[#:tag "build-cs"]{More Instructions: Building Racket on Chez Scheme}
The @exec{make cs} target (or @exec{make cs-as-is} for a rebuild, or
@exec{nmake win32-cs} on Windows with Visual Studio) builds a variant
@exec{nmake win-cs} on Windows with Visual Studio) builds a variant
of Racket that runs on Chez Scheme. By default, the executables for
the Racket CS variant all have a @litchar{cs} or @litchar{CS}
suffix, and they coexist with a Racket BC build by keeping
@ -247,7 +245,7 @@ Instead of using the top-level makefile, you can go into
which gives you more configuration options.
If you don't want any special configuration and you just want the base
build, you can use @exec{make base} (or @exec{nmake win32-base}) with the
build, you can use @exec{make base} (or @exec{nmake win-base}) with the
top-level makefile.
Minimal Racket does not require additional native libraries to run,
@ -263,7 +261,7 @@ minimal-Racket libraries. See the documentation for @exec{raco setup}
for information on the options.
For cross compilation, add configuration options to
@exec{CONFIGURE_ARGS_qq="@nonterm{options}"} as described in the
@exec{CONFIGURE_ARGS="@nonterm{options}"} as described in the
@filepath{README.txt} of @filepath{racket/src}, but also add a
@exec{PLAIN_RACKET=...} argument for the top-level makefile to specify
the same executable as in an @exec{--enable-racket=...} for

View File

@ -381,6 +381,10 @@ flags:
@itemize[
@item{@FlagFirst{Z} : The argument following this flag is ignored.
This flag can be handy in some impoverished scripting environments
to replace or cancel another command-line argument.}
@item{@FlagFirst{-} : No argument following this flag is itself used
as a flag.}
@ -462,7 +466,8 @@ Extra arguments following the last option are available from the
@indexed-racket[current-command-line-arguments] parameter.
@history[#:changed "6.90.0.17" @elem{Added @Flag{O}/@DFlag{stdout}.}
#:changed "7.1.0.5" @elem{Added @Flag{M}/@DFlag{compile-any}.}]
#:changed "7.1.0.5" @elem{Added @Flag{M}/@DFlag{compile-any}.}
#:changed "7.8.0.6" @elem{Added @Flag{Z}.}]
@; ----------------------------------------------------------------------

View File

@ -112,16 +112,26 @@ local/Makefile:
# Install (common) ----------------------------------------
# Intended for configuration by an external makefile that drives this one:
SELF_ROOT_CONFIG_FLAG = -Z
SELF_ROOT_CONFIG_DIR = ../../../build/config
SELF_ROOT_CONFIG = $(SELF_ROOT_CONFIG_FLAG) $(SELF_ROOT_CONFIG_DIR)
INST_CONFIG = -X @DIRCVTPRE@"$(DESTDIR)$(collectsdir)"@DIRCVTPOST@ -G @DIRCVTPRE@"$(DESTDIR)$(configdir)"@DIRCVTPOST@
SETUP_RACKET_FLAGS = $(INST_CONFIG) $(SETUP_MACHINE_FLAGS) $(SELF_RACKET_FLAGS) @INSTALL_SETUP_RACKET_FLAGS@
SETUP_RACKET_FLAGS = $(INST_CONFIG) $(SETUP_MACHINE_FLAGS) $(SELF_ROOT_CONFIG) @INSTALL_SETUP_RACKET_FLAGS@
SETUP_SETUP_FLAGS = @INSTALL_SETUP_FLAGS@ $(PLT_SETUP_OPTIONS) $(PLT_ISO)
SETUP_ARGS = $(SETUP_RACKET_FLAGS) -N "raco" -l- setup $(SETUP_SETUP_FLAGS)
# Needed for non-GNU makes:
PROPAGATE_VARIABLES = SELF_ROOT_CONFIG_FLAG="$(SELF_ROOT_CONFIG_FLAG)" \
PLT_SETUP_OPTIONS="$(PLT_SETUP_OPTIONS)" \
SETUP_MACHINE_FLAGS="$(SETUP_MACHINE_FLAGS)"
install:
$(MAKE) install-@MAIN_MAKE_TARGET@
$(MAKE) install-@MAIN_MAKE_TARGET@ $(PROPAGATE_VARIABLES)
install-racket-variant:
$(MAKE) install-@MAIN_VARIANT@
$(MAKE) install-@MAIN_VARIANT@ $(PROPAGATE_VARIABLES)
plain-install:
if [ "$(DESTDIR)" != "" ]; then \
@ -264,17 +274,17 @@ install-cs-common:
$(MAKE) install-common-first
cd cs/c; $(MAKE) plain-install
$(MAKE) install-common-middle MIDDLE_POST_COLLECTS=install-no-post-collects
cd cs/c; $(MAKE) $(CS_SETUP_INSTALL_TARGET)
cd cs/c; $(MAKE) $(CS_SETUP_INSTALL_TARGET) $(PROPAGATE_VARIABLES)
$(MAKE) install-common-last
install-racketcs:
$(MAKE) install-cs-common
$(MAKE) install-cs-common $(PROPAGATE_VARIABLES)
plain-install-racketcs:
$(MAKE) install-cs-common CS_SETUP_INSTALL_TARGET=no-setup-install
install-cs:
$(MAKE) install-racketcs
$(MAKE) install-racketcs $(PROPAGATE_VARIABLES)
plain-install-cs:
$(MAKE) plain-install-racketcs

View File

@ -409,22 +409,32 @@ repack-no-install-libs:
# ----------------------------------------
# Install
install@NOT_MINGW@:
$(MAKE) plain-install
$(MAKE) setup-install
# RUN_RACKET typically redirects to RUN_THIS_RACKET, but it can also
# redirect to a compatible existing Racket executable (e.g., for
# cross-compilation)
RUN_THIS_RACKET = $(DESTDIR)$(bindir)/racket$(CS_INSTALLED)
# Intended for configuration by an external makefile that drives this one:
SELF_ROOT_CONFIG_FLAG = -Z
SELF_ROOT_CONFIG_DIR = ../../../../../build/config
SELF_ROOT_CONFIG = $(SELF_ROOT_CONFIG_FLAG) $(SELF_ROOT_CONFIG_DIR)
INST_CONFIG = -X "$(DESTDIR)$(collectsdir)" -G "$(DESTDIR)$(configdir)"
SETUP_RACKET_FLAGS = $(INST_CONFIG) $(SETUP_MACHINE_FLAGS) $(SELF_RACKET_FLAGS) @INSTALL_SETUP_RACKET_FLAGS@
SETUP_RACKET_FLAGS = $(INST_CONFIG) $(SETUP_MACHINE_FLAGS) $(SELF_ROOT_CONFIG) @INSTALL_SETUP_RACKET_FLAGS@
SETUP_SETUP_FLAGS = @INSTALL_SETUP_FLAGS@ $(PLT_SETUP_OPTIONS) $(PLT_ISO)
SETUP_ARGS = $(SETUP_RACKET_FLAGS) -N "raco" -l- setup $(SETUP_SETUP_FLAGS)
# Needed for non-GNU makes:
PROPAGATE_VARIABLES = SELF_ROOT_CONFIG_FLAG="$(SELF_ROOT_CONFIG_FLAG)" \
PLT_SETUP_OPTIONS="$(PLT_SETUP_OPTIONS)" \
SETUP_MACHINE_FLAGS="$(SETUP_MACHINE_FLAGS)"
install@NOT_MINGW@:
$(MAKE) plain-install
$(MAKE) setup-install $(PROPAGATE_VARIABLES)
setup-install:
@RUN_RACKET@ $(SELF_RACKET_FLAGS) $(SETUP_ARGS)
@RUN_RACKET@ $(SELF_ROOT_CONFIG) $(SETUP_ARGS)
no-setup-install:
echo done

View File

@ -4890,10 +4890,10 @@ if test "${enable_racket}" != "" ; then
# Racket used only for generating Chez Scheme boot files
if test "${CROSS_MODE}" = "cross" ; then
RACKET="${enable_racket}"
SETUP_BOOT_MODE=--chain
else
BOOTFILE_RACKET="${enable_racket}"
fi
SETUP_BOOT_MODE=--chain
fi
if test "${enable_compress}" != "no" ; then

View File

@ -439,10 +439,10 @@ if test "${enable_racket}" != "" ; then
# Racket used only for generating Chez Scheme boot files
if test "${CROSS_MODE}" = "cross" ; then
RACKET="${enable_racket}"
SETUP_BOOT_MODE=--chain
else
BOOTFILE_RACKET="${enable_racket}"
fi
SETUP_BOOT_MODE=--chain
fi
if test "${enable_compress}" != "no" ; then

View File

@ -556,6 +556,9 @@
(flags-loop null (see saw 'non-config))]
[("-j" "--no-jit")
(loop (cdr args))]
[("-Z")
(let-values ([(ignored rest-args) (next-arg "argument to ignore" arg within-arg args)])
(flags-loop rest-args saw))]
[("-h" "--help")
(show-help)
(exit)]

269
racket/src/makemake.rkt Normal file
View File

@ -0,0 +1,269 @@
#lang racket/base
(require racket/cmdline)
(define src
(command-line
#:args (src) src))
(printf "# Generated from ~s\n" src)
(define statics '(MAKE))
(define vars (make-hasheq))
(define target-uses (make-hasheq))
;; each call is `(cons <target> <vars>)` for
;; variables explicitly propagated
(define target-calls (make-hasheq))
(define (hash-sorted-keys ht)
(hash-map ht (lambda (k v) k) #t))
(define (read-make-line in)
(define l (read-line in))
(cond
[(eof-object? l) l]
[(regexp-match? #rx"\\\\$" l)
(string-append (substring l 0 (sub1 (string-length l)))
(read-make-line in))]
[else l]))
(define (match->symbol m)
(define s (cadr m))
(string->symbol (if (string? s)
s
(bytes->string/utf-8 s))))
(define target-macros '())
(define (match->called-targets m)
(define s (let ([s (cadr m)])
(if (string? s)
s
(bytes->string/utf-8 s))))
(let loop ([s s])
(let t-loop ([tms target-macros])
(cond
[(null? tms) (list (string->symbol s))]
[(regexp-match? (caar tms) s)
(apply append
(for/list ([alt (cdar tms)])
(loop (regexp-replace (caar tms) s alt))))]
[else (t-loop (cdr tms))]))))
(define macros '())
(define (expand-macros s)
(if (eof-object? s)
s
(for/fold ([s s]) ([rx+val (in-list macros)])
(regexp-replace* (car rx+val) s (cdr rx+val)))))
(define rx:macro #rx"^([A-Za-z_0-9]+) *==(.*)$")
(define rx:static #rx"^([A-Za-z_0-9]+) *:=(.*)$")
(define rx:var #rx"^([A-Za-z_0-9]+) *=")
(define rx:target #rx"^([-A-Za-z_0-9/\\.]+) *:")
(define rx:cd-make #rx"^[ \t]*cd +[-a-zA-Z0-9_/\\.]+ +[&][&] +[$][(]MAKE[)]")
(define rx:make #rx"[$][(]MAKE[)] +(?:-j +[^ ]+ +)?([-a-zA-Z0-9/_$().]+)")
(define rx:assign #rx"^ +([-_A-Za-z0-9]+)=(\"[^\"]*\"|'[^\']*'|[^ ]*)")
(define (parse in)
(define (declare-variable! m)
(define var (match->symbol m))
(when (hash-ref vars var #f)
(printf "variable redefined: ~a\n" var))
(hash-set! vars var #t)
var)
(let loop ([target #f])
(define l (expand-macros (read-make-line in)))
(cond
[(eof-object? l) (void)]
[(regexp-match #rx"^# Target selector: (.*)$" l)
=> (lambda (sel-m)
(define next-l (read-make-line in))
(cond
[(regexp-match rx:var next-l)
=> (lambda (m)
(define var (declare-variable! m))
(define alts
(let ([i (open-input-string (cadr sel-m))])
(let loop ()
(cond
[(regexp-match #rx"`([^`]*)`" i)
=> (lambda (m)
(cons (bytes->string/utf-8 (cadr m)) (loop)))]
[else '()]))))
(printf "# Target values for ~a: ~s\n" var alts)
(set! target-macros (cons (cons (regexp (format "[$][(]~a[)]" var))
alts)
target-macros))
(loop #f))]
[else (error "did not find variable for target selector")]))]
[(or (regexp-match? #rx"^#.*" l)
(regexp-match? #rx"^ *$" l))
(loop target)]
[(regexp-match rx:macro l)
=> (lambda (m)
(define macro (match->symbol m))
(when (assq macro macros)
(printf "macro redefined: ~a\n" macro))
(set! macros (cons (cons (regexp (format "[$][(]~a[)]" macro)) (regexp-replace-quote (caddr m)))
macros))
(loop #f))]
[(regexp-match rx:static l)
=> (lambda (m)
(set! statics (cons (match->symbol m) statics))
(loop #f))]
[(regexp-match rx:var l)
=> (lambda (m)
(declare-variable! m)
(loop #f))]
[(regexp-match rx:target l)
=> (lambda (m)
(define target (match->symbol m))
(when (hash-ref target-uses target #f)
(printf "target redefined: ~a\n" target))
(hash-set! target-uses target '())
(hash-set! target-calls target '())
(loop target))]
[(regexp-match? #rx"^\t" l)
(let ([i (open-input-string l)])
(let loop ()
(cond
[(regexp-try-match rx:cd-make i)
;; ignore `make` target in a different directory
(loop)]
[else
(define m (regexp-match rx:make i))
(when m
(for ([called-target (match->called-targets m)])
(define vars (let loop ()
(cond
[(regexp-try-match rx:assign i)
=> (lambda (m)
(cons (match->symbol m) (loop)))]
[else '()])))
(hash-set! target-calls target
(cons (cons called-target vars)
(hash-ref target-calls target '()))))
(loop))])))
(let ([i (open-input-string l)])
(let loop ()
(define m (regexp-match #rx"[$][(]([-_A-Za-z0-9]+)[)]" i))
(when m
(define var (match->symbol m))
(define vars (hash-ref target-uses target '()))
(unless (or (memq var vars)
(memq var statics))
(hash-set! target-uses target (cons var vars)))
(loop))))
(loop target)]
[else
(printf "## unparsed: ~s\n" l)
(loop target)])))
(define (less-space s)
(regexp-replace* #rx"\t *| +" s " "))
(define (variables in)
(let loop ()
(define l (expand-macros (read-make-line in)))
(cond
[(eof-object? l) (void)]
[(regexp-match rx:macro l)
=> (lambda (m)
(printf "~a = ~a\n" (cadr m) (less-space (caddr m)))
(loop))]
[(regexp-match? rx:static l)
(displayln (less-space (regexp-replace #rx":=" l "=")))
(loop)]
[(regexp-match? rx:var l)
(displayln (less-space l))
(loop)]
[(regexp-match? rx:target l)
(loop)]
[(regexp-match? #rx"^\t" l)
(loop)]
[else
(loop)])))
(define (convert in)
(let loop ([target #f])
(define l (expand-macros (read-make-line in)))
(cond
[(eof-object? l) (void)]
[(or (regexp-match? #rx"^#.*" l)
(regexp-match? #rx"^ *$" l))
(loop target)]
[(regexp-match? rx:macro l)
(loop #f)]
[(regexp-match? rx:static l)
(loop #f)]
[(regexp-match? rx:var l)
(loop #f)]
[(regexp-match rx:target l)
=> (lambda (m)
(define target (match->symbol m))
(displayln l)
(loop target))]
[(regexp-match? #rx"^\t" l)
(let ([i (open-input-string l)])
(let loop ()
(cond
[(regexp-try-match rx:cd-make i 0 #f (current-output-port))
=> (lambda (m)
;; ignore `make` target in a different directory
(display (car m))
(loop))]
[else
(define m (regexp-match rx:make i 0 #f (current-output-port)))
(when m
(display (car m))
(define called-targets (match->called-targets m))
(define vars (let loop ()
(cond
[(regexp-try-match rx:assign i 0 #f (current-output-port))
=> (lambda (m)
(printf " ~a=~a" (cadr m) (caddr m))
(cons (match->symbol m) (loop)))]
[else '()])))
(define want-vars (remove* vars
(hash-sorted-keys
(for*/hasheq ([called-target (in-list called-targets)]
[var (in-list (hash-ref target-uses called-target '()))])
(values var #t)))))
(for ([want-var (in-list want-vars)])
(printf " ~a=\"$(~a)\"" want-var want-var))
(loop))])))
(newline)
(loop target)]
[else
(loop target)])))
(call-with-input-file* src parse)
(let loop ([complain? #t])
(define orig (hash-copy target-uses))
(for ([(target calls) (in-hash target-calls)])
(for ([call (in-list calls)])
(define called-target (car call))
(define vars (cdr call))
(when complain?
(unless (hash-ref target-uses called-target #f)
(printf "## ~s -> ~s missing\n" target called-target)))
(define want-vars (hash-ref target-uses called-target '()))
(for ([want-var (in-list want-vars)])
(unless (memq want-var statics)
(unless (memq want-var vars)
(define need-vars (hash-ref target-uses target '()))
(unless (memq want-var need-vars)
(hash-set! target-uses target (cons want-var need-vars))))))))
(unless (equal? orig target-uses)
(loop #f)))
(call-with-input-file* src variables)
(call-with-input-file* src convert)

View File

@ -347,7 +347,7 @@ cstartup_:
# Running "cify-startup.rkt" through "$(SETUP_BOOT)" generates more
# dependencies in "cstartup.d" for `$(CSTARTUPDEST)`
$(CSTARTUPDEST): $(srcdir)/src/startup.inc $(srcdir)/src/schvers.h
@RUN_RACKET_CGC@ $(SELF_RACKET_FLAGS) $(SETUP_BOOT) cstartup.inc cstartup.d $(srcdir)/src/cify-startup.rkt $(srcdir)/src/startup.inc $(srcdir)/src/schvers.h
@RUN_RACKET_CGC@ $(SETUP_BOOT) cstartup.inc cstartup.d $(srcdir)/src/cify-startup.rkt $(srcdir)/src/startup.inc $(srcdir)/src/schvers.h
@INCLUDEDEP@ cstartup.d
mark:

View File

@ -1124,6 +1124,17 @@ static int run_from_cmd_line(int argc, char *_argv[],
argv++;
was_config_flag = 1;
break;
case 'Z':
if (argc < 2) {
PRINTF("%s: missing argument to ignore after %s switch\n",
prog,
real_switch);
goto show_need_help;
}
--argc;
argv++;
was_config_flag = 1;
break;
default:
specific_switch[0] = *str;
specific_switch[1] = 0;
@ -1432,6 +1443,7 @@ static int run_from_cmd_line(int argc, char *_argv[],
" -L <levels>, --syslog <levels> : Set syslog logging to <levels>\n"
" Meta options:\n"
" -- : No argument following this switch is used as a switch\n"
" -Z : Ignore the argument following this switch\n"
" -h, --help : Show this information and exits, ignoring other options\n"
"Default options:\n"
" If only configuration options are provided, -i is added\n"