git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: "René Scharfe" <l.s.r@web.de>
To: "Ævar Arnfjörð Bjarmason" <avarab@gmail.com>
Cc: Git List <git@vger.kernel.org>,
	Junio C Hamano <gitster@pobox.com>, Taylor Blau <me@ttaylorr.com>,
	Jeff King <peff@peff.net>
Subject: Re: [PATCH 0/8] run-command: remove run_command_v_*()
Date: Fri, 28 Oct 2022 18:04:41 +0200	[thread overview]
Message-ID: <749f6adc-928a-0978-e3a1-2ede9f07def0@web.de> (raw)
In-Reply-To: <221028.86edusan0k.gmgdl@evledraar.gmail.com>

Am 27.10.22 um 23:46 schrieb Ævar Arnfjörð Bjarmason:
>
> On Thu, Oct 27 2022, René Scharfe wrote:
>
>> Replace the convenience functions run_command_v_opt() et. al. and use
>> struct child_process and run_command() directly instead, for an overall
>> code reduction and a simpler and more flexible API that allows creating
>> argument lists without magic numbers and reduced risk of memory leaks.
>>
>> This is a broken-out and polished version of the original scratch at
>> https://lore.kernel.org/git/9d924a5d-5c72-fbe6-270c-a8f6c5fc5850@web.de/
>>
>> Ævar Arnfjörð Bjarmason (1):
>>   merge: remove always-the-same "verbose" arguments
>>
>> René Scharfe (7):
>>   bisect--helper: factor out do_bisect_run()
>>   use child_process members "args" and "env" directly
>>   use child_process member "args" instead of string array variable
>>   replace and remove run_command_v_opt_cd_env()
>>   replace and remove run_command_v_opt_tr2()
>>   replace and remove run_command_v_opt_cd_env_tr2()
>>   replace and remove run_command_v_opt()
>
> Even though I had a an earlier alternate series series[1] for this I'd
> be happy to see this version go in. I left some comments here and there,
> but with/without a re-roll am happy to give this:
>
> 	Reviewed-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
>
> I think I would have just gone for this in the first place, but thought
> that people loved the convenience functions too much. It can be hard to
> judge sentiments in advance :)
>
> One reason I hadn't re-submitted something is that there were
> outstanding new conflicts with "seen", and I see from applying this
> topic & merging it that it conflicts somewhat heavily. Junio seems to be
> on-board with this though, so maybe he won't mind.
>
> I didn't see any glaring instances where this made things worse, so
> maybe we didn't need these convenience wrappers in the first place.

Right, and I think this is important.

>
> But from the earlier discussion it does seema bit like we tossed the
> very notion out because some didn't like the duplicating of struct
> members with the flags (which I also doen't like).
>
> So I came up with the below experiment on top, it's not an attempt to
> convert all callers, just a demo.
>
> Maybe you think some ideas here are worth using, I probably won't pursue
> it (but maybe as ideas for some other future API).
>
> It's a combination of stuff, some of which you might like, some not,
> namely:
>
> - Have the API work the same way, but just have a run_commandl(&opt,
>   ...) in addition to the run_command(). It's just a trivial wrapper to
>   push stuff into &cmd.args for you.
>
> - I saw a few callers that could have perhaps used a similarly trivial
>   run_commandv(), but that doesn't benefit from LAST_ARG_MUST_BE_NULL,
>   so I didn't bother.

I thought about that as well, but at least for me is probably a just a
case of loss aversion, which I have aplenty.  run_command_v_opt() alone
isn't bad and patch 8 on its own probably wouldn't fly, so I mentally
cling to it.  But without it the API is untangled and simpler.  Only a
single way to specify flags and arguments, no shortcuts for special
combinations.  Overall easier to use once we forget the old ways.

>
> - I wish C had a nicer syntax for not just declaring but squashing
>   together compile_time bracketed lists (think set operations). But the
>   below "CHILD_PROCESS_INIT_LIST" is a pretty good poor man's version.
>
>   I see gcc/clang nicely give us safety rails for that with
>   "-Woverride-init", for this sort of "opts struct with internal stuff,
>   but also user options" I think it works out very nicely.
>

That's a nice and simple macro.  I played with a gross variant à la

  #define CHILD_PROCESS_INIT_EX(...) { .args = STRVEC_INIT, __VA_ARGS__ }

which would allow e.g.

  struct child_process cmd = CHILD_PROCESS_INIT_EX(.git_cmd = 1);

Yours is better, but they share the downside of not actually saving any
lines of code..

> - We have quite a few callers that want "on error, die", so maybe we
>   should have something like that "on_error" sooner than later.

We could add a die_on_error bit for that, or start_command_or_die() and
run_command_or_die() variants (there I go again, multiplying APIs..).
They could report the failed command, which a caller can't do because
the internal strvec is already cleared once it learns of the failure.

>
> On clever (but perhaps overly clever) thing I didn't use, but played
> with recently in another context, is that now with C99 you can also do:
>
> 	int run_commandl(struct child_process *cmd, ...);
> 	#define run_command(...) run_command_1(__VA_ARGS__, NULL)
>
> I.e. make the API itself support all of:
>
> 	run_command(&cmd);
> 	run_command(&cmd, "reboot");
> 	run_command(&cmd, "reboot", NULL);
>
> I haven't made up my mind on whether that's just overly clever, or
> outright insane :)

Neither, I'd say.  By combining two operations it's less flexible than
a pure run method plus direct access to a strvec member.  It's a
shortcut that may save a line but requires more effort to extend its
callers, e.g. to conditionally add a new argument.

>
> diff --git a/bisect.c b/bisect.c
> index ec7487e6836..ef4f80650f7 100644
> --- a/bisect.c
> +++ b/bisect.c
> @@ -740,9 +740,8 @@ enum bisect_error bisect_checkout(const struct object_id *bisect_rev,
>  		struct child_process cmd = CHILD_PROCESS_INIT;
>
>  		cmd.git_cmd = 1;
> -		strvec_pushl(&cmd.args, "checkout", "-q",
> -			     oid_to_hex(bisect_rev), "--", NULL);
> -		if (run_command(&cmd))
> +		if (run_commandl(&cmd, "checkout", "-q",
> +				 oid_to_hex(bisect_rev), "--", NULL))
>  			/*
>  			 * Errors in `run_command()` itself, signaled by res < 0,
>  			 * and errors in the child process, signaled by res > 0
> diff --git a/builtin/am.c b/builtin/am.c
> index 20aea0d2487..3b7df32ce22 100644
> --- a/builtin/am.c
> +++ b/builtin/am.c
> @@ -2189,10 +2189,9 @@ static int show_patch(struct am_state *state, enum show_patch_type sub_mode)
>  	if (!is_null_oid(&state->orig_commit)) {
>  		struct child_process cmd = CHILD_PROCESS_INIT;
>
> -		strvec_pushl(&cmd.args, "show", oid_to_hex(&state->orig_commit),
> -			     "--", NULL);
>  		cmd.git_cmd = 1;
> -		return run_command(&cmd);
> +		return run_commandl(&cmd, "show", oid_to_hex(&state->orig_commit),
> +				    "--", NULL);
>  	}
>
>  	switch (sub_mode) {
> diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
> index 1d2ce8a0e12..087d21c614a 100644
> --- a/builtin/bisect--helper.c
> +++ b/builtin/bisect--helper.c
> @@ -764,12 +764,12 @@ static enum bisect_error bisect_start(struct bisect_terms *terms, const char **a
>  		strbuf_read_file(&start_head, git_path_bisect_start(), 0);
>  		strbuf_trim(&start_head);
>  		if (!no_checkout) {
> -			struct child_process cmd = CHILD_PROCESS_INIT;
> -
> -			cmd.git_cmd = 1;
> -			strvec_pushl(&cmd.args, "checkout", start_head.buf,
> -				     "--", NULL);
> -			if (run_command(&cmd)) {
> +			struct child_process cmd = {
> +				CHILD_PROCESS_INIT_LIST,
> +				.git_cmd = 1,
> +			};
> +			if (run_commandl(&cmd, "checkout", start_head.buf,
> +					 "--", NULL)) {
>  				res = error(_("checking out '%s' failed."
>  						 " Try 'git bisect start "
>  						 "<valid-branch>'."),
> @@ -1147,8 +1147,7 @@ static int do_bisect_run(const char *command)
>
>  	printf(_("running %s\n"), command);
>  	cmd.use_shell = 1;
> -	strvec_push(&cmd.args, command);
> -	return run_command(&cmd);
> +	return run_commandl(&cmd, command, NULL);
>  }
>
>  static int verify_good(const struct bisect_terms *terms, const char *command)
> diff --git a/builtin/clone.c b/builtin/clone.c
> index 0e4348686b6..80d09e0fbf1 100644
> --- a/builtin/clone.c
> +++ b/builtin/clone.c
> @@ -655,7 +655,6 @@ static int git_sparse_checkout_init(const char *repo)
>  {
>  	struct child_process cmd = CHILD_PROCESS_INIT;
>  	int result = 0;
> -	strvec_pushl(&cmd.args, "-C", repo, "sparse-checkout", "set", NULL);
>
>  	/*
>  	 * We must apply the setting in the current process
> @@ -664,7 +663,7 @@ static int git_sparse_checkout_init(const char *repo)
>  	core_apply_sparse_checkout = 1;
>
>  	cmd.git_cmd = 1;
> -	if (run_command(&cmd)) {
> +	if (run_commandl(&cmd, "-C", repo, "sparse-checkout", "set", NULL)) {
>  		error(_("failed to initialize sparse-checkout"));
>  		result = 1;
>  	}
> @@ -868,13 +867,14 @@ static void dissociate_from_references(void)
>  	char *alternates = git_pathdup("objects/info/alternates");
>
>  	if (!access(alternates, F_OK)) {
> -		struct child_process cmd = CHILD_PROCESS_INIT;
> -
> -		cmd.git_cmd = 1;
> -		cmd.no_stdin = 1;
> -		strvec_pushl(&cmd.args, "repack", "-a", "-d", NULL);
> -		if (run_command(&cmd))
> -			die(_("cannot repack to clean up"));
> +		struct child_process cmd = {
> +			CHILD_PROCESS_INIT_LIST,
> +			.git_cmd = 1,
> +			.no_stdin = 1,
> +			.on_error = CHILD_PROCESS_ON_ERROR_DIE,
> +		};
> +
> +		run_commandl(&cmd, "repack", "-a", "-d", NULL);
>  		if (unlink(alternates) && errno != ENOENT)
>  			die_errno(_("cannot unlink temporary alternates file"));
>  	}
> diff --git a/builtin/difftool.c b/builtin/difftool.c
> index d7f08c8a7fa..b4165b5a8ae 100644
> --- a/builtin/difftool.c
> +++ b/builtin/difftool.c
> @@ -44,11 +44,12 @@ static int difftool_config(const char *var, const char *value, void *cb)
>
>  static int print_tool_help(void)
>  {
> -	struct child_process cmd = CHILD_PROCESS_INIT;
> -
> -	cmd.git_cmd = 1;
> -	strvec_pushl(&cmd.args, "mergetool", "--tool-help=diff", NULL);
> -	return run_command(&cmd);
> +	struct child_process cmd = {
> +		CHILD_PROCESS_INIT_LIST,
> +		.git_cmd = 1,
> +	};
> +
> +	return run_commandl(&cmd, "mergetool", "--tool-help=diff", NULL);
>  }
>
>  static int parse_index_info(char *p, int *mode1, int *mode2,
> diff --git a/git.c b/git.c
> index 6662548986f..93179f44f78 100644
> --- a/git.c
> +++ b/git.c
> @@ -724,7 +724,13 @@ static void handle_builtin(int argc, const char **argv)
>
>  static void execv_dashed_external(const char **argv)
>  {
> -	struct child_process cmd = CHILD_PROCESS_INIT;
> +	struct child_process cmd = {
> +		CHILD_PROCESS_INIT_LIST,
> +		.clean_on_exit = 1,
> +		.wait_after_clean = 1,
> +		.silent_exec_failure = 1,
> +		.trace2_child_class = "dashed",
> +	};
>  	int status;
>
>  	if (get_super_prefix())
> @@ -736,10 +742,6 @@ static void execv_dashed_external(const char **argv)
>
>  	strvec_pushf(&cmd.args, "git-%s", argv[0]);
>  	strvec_pushv(&cmd.args, argv + 1);
> -	cmd.clean_on_exit = 1;
> -	cmd.wait_after_clean = 1;
> -	cmd.silent_exec_failure = 1;
> -	cmd.trace2_child_class = "dashed";
>
>  	trace2_cmd_name("_run_dashed_");
>
> diff --git a/run-command.c b/run-command.c
> index 23e100dffc4..4b20aa1b577 100644
> --- a/run-command.c
> +++ b/run-command.c
> @@ -993,15 +993,45 @@ int finish_command_in_signal(struct child_process *cmd)
>
>  int run_command(struct child_process *cmd)
>  {
> -	int code;
> +	int starting = 1;
> +	int code = 0;
>
>  	if (cmd->out < 0 || cmd->err < 0)
>  		BUG("run_command with a pipe can cause deadlock");
>
>  	code = start_command(cmd);
>  	if (code)
> +		goto error;
> +	starting = 0;
> +	code = finish_command(cmd);
> +	if (!code)
> +		return 0;
> +error:
> +	switch (cmd->on_error) {
> +	case CHILD_PROCESS_ON_ERROR_DIE:
> +		die(starting ?
> +		    _("start_command() for '%s' failed (error code %d)") :
> +		    _("'%s': got non-zero exit code '%d'"),
> +		    cmd->args.v[0], code);
> +		break;
> +	case CHILD_PROCESS_ON_ERROR_RETURN:
>  		return code;
> -	return finish_command(cmd);
> +	default:
> +		BUG("unreachable");
> +	}
> +}
> +
> +int run_commandl(struct child_process *cmd, ...)
> +{
> +	va_list ap;
> +	const char *arg;
> +
> +	va_start(ap, cmd);
> +	while ((arg = va_arg(ap, const char *)))
> +		strvec_push(&cmd->args, arg);
> +	va_end(ap);
> +
> +	return run_command(cmd);
>  }
>
>  #ifndef NO_PTHREADS
> diff --git a/run-command.h b/run-command.h
> index fe2717ad11e..71e390350ed 100644
> --- a/run-command.h
> +++ b/run-command.h
> @@ -15,7 +15,22 @@
>   * produces in the caller in order to process it.
>   */
>
> +enum child_process_on_error {
> +	/**
> +	 * Return a status code from run_command(). Set to 0 so that
> +	 * it'll be { 0 } init'd. If it's specified in a
> +	 * CHILD_PROCESS_INIT_LIST initialization we don't want a
> +	 * "-Woverride-init" warning.
> +	 */
> +	CHILD_PROCESS_ON_ERROR_RETURN = 0,
>
> +	/**
> +	 * die() with some sensible message if run_command() would
> +	 * have returned a non-zero exit code.
> +	 */
> +	CHILD_PROCESS_ON_ERROR_DIE,
> +};
> +
>  /**
>   * This describes the arguments, redirections, and environment of a
>   * command to run in a sub-process.
> @@ -42,6 +57,10 @@
>   *		redirected.
>   */
>  struct child_process {
> +	/**
> +	 * Error behavior, see "enum child_process_on_error" above.
> +	 */
> +	enum child_process_on_error on_error;
>
>  	/**
>  	 * The .args is a `struct strvec', use that API to manipulate
> @@ -144,10 +163,11 @@ struct child_process {
>  	void (*clean_on_exit_handler)(struct child_process *process);
>  };
>
> -#define CHILD_PROCESS_INIT { \
> +#define CHILD_PROCESS_INIT_LIST \
> +	/* .on_error = CHILD_PROCESS_ON_ERROR_RETURN */ \
>  	.args = STRVEC_INIT, \
> -	.env = STRVEC_INIT, \
> -}
> +	.env = STRVEC_INIT
> +#define CHILD_PROCESS_INIT { CHILD_PROCESS_INIT_LIST }
>
>  /**
>   * The functions: child_process_init, start_command, finish_command,
> @@ -218,6 +238,14 @@ int finish_command_in_signal(struct child_process *);
>   */
>  int run_command(struct child_process *);
>
> +/**
> + * Like run_command() but takes a variable number of arguments, which
> + * will be appended with the equivalent of strvec_pushl(&cmd.args,
> + * ...) before invoking run_command().
> + */
> +LAST_ARG_MUST_BE_NULL
> +int run_commandl(struct child_process *cmd, ...);
> +
>  /*
>   * Trigger an auto-gc
>   */

  reply	other threads:[~2022-10-28 16:06 UTC|newest]

Thread overview: 49+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-10-27 16:30 [PATCH 0/8] run-command: remove run_command_v_*() René Scharfe
2022-10-27 16:35 ` [PATCH 1/8] merge: remove always-the-same "verbose" arguments René Scharfe
2022-10-29 18:12   ` Taylor Blau
2022-10-27 16:35 ` [PATCH 2/8] bisect--helper: factor out do_bisect_run() René Scharfe
2022-10-27 22:26   ` Ævar Arnfjörð Bjarmason
2022-10-29 18:16     ` Taylor Blau
2022-10-27 16:36 ` [PATCH 3/8] use child_process members "args" and "env" directly René Scharfe
2022-10-27 18:28   ` Junio C Hamano
2022-10-27 22:37   ` Ævar Arnfjörð Bjarmason
2022-10-27 16:37 ` [PATCH 4/8] use child_process member "args" instead of string array variable René Scharfe
2022-10-27 21:09   ` Ævar Arnfjörð Bjarmason
2022-10-28 14:23     ` René Scharfe
2022-10-29 18:30       ` Taylor Blau
2022-10-29 18:26   ` Taylor Blau
2022-10-27 16:38 ` [PATCH 5/8] replace and remove run_command_v_opt_cd_env() René Scharfe
2022-10-27 20:16   ` Ævar Arnfjörð Bjarmason
2022-10-29 19:26   ` Taylor Blau
2022-10-27 16:39 ` [PATCH 6/8] replace and remove run_command_v_opt_tr2() René Scharfe
2022-10-27 16:40 ` [PATCH 7/8] replace and remove run_command_v_opt_cd_env_tr2() René Scharfe
2022-10-27 22:46   ` Ævar Arnfjörð Bjarmason
2022-10-28 14:23     ` René Scharfe
2022-10-27 16:41 ` [PATCH 8/8] replace and remove run_command_v_opt() René Scharfe
2022-10-27 22:41   ` Ævar Arnfjörð Bjarmason
2022-10-28 14:23     ` René Scharfe
2022-10-27 20:11 ` [PATCH 0/8] run-command: remove run_command_v_*() Jeff King
2022-10-28 14:23   ` René Scharfe
2022-10-27 21:46 ` Ævar Arnfjörð Bjarmason
2022-10-28 16:04   ` René Scharfe [this message]
2022-10-28 16:11     ` Ævar Arnfjörð Bjarmason
2022-10-28 17:16       ` René Scharfe
2022-10-29  2:17         ` Ævar Arnfjörð Bjarmason
2022-10-29 10:05           ` René Scharfe
2022-10-29 19:32 ` Taylor Blau
2022-10-30 11:40 ` [PATCH v2 0/12] " René Scharfe
2022-10-30 11:42   ` [PATCH v2 01/12] merge: remove always-the-same "verbose" arguments René Scharfe
2022-10-30 11:45   ` [PATCH v2 02/12] run-command: fix return value comment René Scharfe
2022-10-30 11:46   ` [PATCH v2 03/12] am: simplify building "show" argument list René Scharfe
2022-10-30 11:47   ` [PATCH v2 04/12] bisect: simplify building "checkout" " René Scharfe
2022-10-30 11:48   ` [PATCH v2 05/12] bisect--helper: factor out do_bisect_run() René Scharfe
2022-10-30 11:49   ` [PATCH v2 06/12] sequencer: simplify building argument list in do_exec() René Scharfe
2022-10-30 11:50   ` [PATCH v2 07/12] use child_process member "args" instead of string array variable René Scharfe
2022-10-30 11:51   ` [PATCH v2 08/12] use child_process members "args" and "env" directly René Scharfe
2022-10-30 11:51   ` [PATCH v2 09/12] replace and remove run_command_v_opt_cd_env() René Scharfe
2022-10-30 11:52   ` [PATCH v2 10/12] replace and remove run_command_v_opt_tr2() René Scharfe
2022-10-30 11:53   ` [PATCH v2 11/12] replace and remove run_command_v_opt_cd_env_tr2() René Scharfe
2022-10-30 11:55   ` [PATCH v2 12/12] replace and remove run_command_v_opt() René Scharfe
2022-10-30 11:58   ` [PATCH v2 0/12] run-command: remove run_command_v_*() René Scharfe
2022-10-30 18:05     ` Taylor Blau
2022-10-31 13:35       ` Ævar Arnfjörð Bjarmason

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=749f6adc-928a-0978-e3a1-2ede9f07def0@web.de \
    --to=l.s.r@web.de \
    --cc=avarab@gmail.com \
    --cc=git@vger.kernel.org \
    --cc=gitster@pobox.com \
    --cc=me@ttaylorr.com \
    --cc=peff@peff.net \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).