git@vger.kernel.org mailing list mirror (one of many)
 help / color / mirror / code / Atom feed
From: "Ævar Arnfjörð Bjarmason" <avarab@gmail.com>
To: "René Scharfe" <l.s.r@web.de>
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: Thu, 27 Oct 2022 23:46:50 +0200	[thread overview]
Message-ID: <221028.86edusan0k.gmgdl@evledraar.gmail.com> (raw)
In-Reply-To: <7407e074-4bd8-b351-7fa4-baf59b41880c@web.de>


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.

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 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.

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

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 :)

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
  */

  parent reply	other threads:[~2022-10-27 23:02 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 [this message]
2022-10-28 16:04   ` René Scharfe
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

  List information: http://vger.kernel.org/majordomo-info.html

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

  git send-email \
    --in-reply-to=221028.86edusan0k.gmgdl@evledraar.gmail.com \
    --to=avarab@gmail.com \
    --cc=git@vger.kernel.org \
    --cc=gitster@pobox.com \
    --cc=l.s.r@web.de \
    --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.
Code repositories for project(s) associated with this public inbox

	https://80x24.org/mirrors/git.git

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 read-only IMAP folder(s) and NNTP newsgroup(s).