From patchwork Tue Jan 28 22:01:37 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Josh Steadmon X-Patchwork-Id: 13953214 Received: from mail-pj1-f73.google.com (mail-pj1-f73.google.com [209.85.216.73]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 04C6F19ADA2 for ; Tue, 28 Jan 2025 22:01:44 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.73 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1738101706; cv=none; b=C5Wh40nmiDlDZOZD1ZSOomhEY7s891ZRHdpkArC4jEsYB0jfnT9UYtYO9lxQ4wv4JiKpqm1uMXAJrM673t83dp6oOFWSSjB4xix3ZuYNH4q0lAA64DA2og6fj2ZszhXH6kgeS/PboYMu7+7i+dEhk3PAEMe+lBil4ivFiRwV5sI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1738101706; c=relaxed/simple; bh=xpQ5r3b+wKyJStBtFVyIeSlIbJg+wtr2luHDlzGJRy4=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=N3ZS/4uqRB8+KAhzzS+iUDTR0UZnvXe/YB91BNPqv3pgvs46ywO+vcZSrHIq9H85J6ld2C7NEN/Q6Bob+08xCaNtRNWFgK02a5ywKpwT5z3MiCYg10ZzIR/VPLnoYD+Euur8DgAo5A2sDWltKtU/t5foth3PqAxwNjTXYjT1k9k= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--steadmon.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=qUf0ID8g; arc=none smtp.client-ip=209.85.216.73 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--steadmon.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="qUf0ID8g" Received: by mail-pj1-f73.google.com with SMTP id 98e67ed59e1d1-2ef9864e006so17865095a91.2 for ; Tue, 28 Jan 2025 14:01:44 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1738101704; x=1738706504; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=u9I/fPoi91qaez+oEvBEfPK30HgUg9/3UbRce4Ryttw=; b=qUf0ID8gkIDH6XAo9Qj5vXB8g4tco4IneEDloT9UrkYoEMiwFkZmWCn0zPTAPrlGDz ehhBQVPjMQIujGG9WbtemE4+VUoEoWEQoSGZVztRc1JEQ8DT8e3CHkNpCi6DQJesuIaJ 1iGL5f+RXf8v2e2C7JRw4jxFxLjVPFI26F2410k0zJmc3ZqTJmO/lvw05M8Nd7VkRntZ taGcbpX8pIuPezaiR/9uPBGHHBlxU43ZOtRTBlEgiBvJVfj8ixoCX2FJE3kKc6QDs0m7 tNqK42cwLlUvKF6GR/ft+NspXiVz7utVxFwVj130wJqWB8LX+4SkR+O9kMFQkNlwXBQc REaA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1738101704; x=1738706504; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=u9I/fPoi91qaez+oEvBEfPK30HgUg9/3UbRce4Ryttw=; b=AVGUAzBOUitYJgAXREuyUa7d8+diycMifz5vkPv+XUqEWxnJEhYSUokP9n33NWuKd1 JmFlWvHOBbnS/DV3x5Z1tISQnITGyp+T7RNu28zQCCl3Al6F51i6YD7k+1OOU+ATk7je wF7PDBFVpQ5VAiRn78x0wT2B2KUmARvDphvR9uE5gxsmLZ6RTB6F1deLc1A/+cFAjJ22 BMCxgJvEfzaDcffryOmCOe6wDpi1dvI5JTkUMYaDd6cJPYYxwdr3HUQyAg5OD+YRmOnl 4nNdxHhE6Krm4aDungCZSLFBLxPIVb9vdkl92xpWSvAEAEYVmxuny8pMF5rNpFAPphc7 041A== X-Gm-Message-State: AOJu0YyXjeyuU5Uyyq8/o4ZqjE+fEKtzPWt+ReKRwJRUQQp1NpE2VKFH 45DWqwoRgsAGnPb2podjh5avLEMer25xCU1xF84fQcH5qIX3oOyJpy+CksWCOfIBV1byMOlQVnN PCuta8ywnf8lvcTjhjQkL2x6WRtjqpbq+1WUOG3pjFn/jzzzrt9XqB0nJL48HThMoLLZCfPP3l1 ixE53tYI+HUJ/XTqYlK5qK3gWkVTzo9TAzBo1C+Eg= X-Google-Smtp-Source: AGHT+IGDgyDL1a6P/Mqeor7DUbSC7QzePqZDwZUJWJQsNFATkpSKj56Kd+Igf7iAs4PLjqcrdHHQ4xo5Gw4sKw== X-Received: from pjbdb3.prod.google.com ([2002:a17:90a:d643:b0:2ef:7aa2:dd51]) (user=steadmon job=prod-delivery.src-stubby-dispatcher) by 2002:a17:90b:2dcd:b0:2ee:d96a:5831 with SMTP id 98e67ed59e1d1-2f83ac801e2mr953534a91.30.1738101704172; Tue, 28 Jan 2025 14:01:44 -0800 (PST) Date: Tue, 28 Jan 2025 14:01:37 -0800 In-Reply-To: Precedence: bulk X-Mailing-List: git@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: X-Mailer: git-send-email 2.48.1.262.g85cc9f2d1e-goog Message-ID: Subject: [PATCH v8 1/4] common-main: split init and exit code into new files From: Josh Steadmon To: git@vger.kernel.org Cc: calvinwan@google.com, nasamuffin@google.com, emrass@google.com, gitster@pobox.com, sandals@crustytoothpaste.net, ps@pks.im, phillip.wood123@gmail.com Currently, object files in libgit.a reference common_exit(), which is contained in common-main.o. However, common-main.o also includes main(), which references cmd_main() in git.o, which in turn depends on all the builtin/*.o objects. We would like to allow external users to link libgit.a without needing to include so many extra objects. Enable this by splitting common_exit() and check_bug_if_BUG() into a new file common-exit.c, and add common-exit.o to LIB_OBJS so that these are included in libgit.a. This split has previously been proposed ([1], [2]) to support fuzz tests and unit tests by avoiding conflicting definitions for main(). However, both of those issues were resolved by other methods of avoiding symbol conflicts. Now we are trying to make libgit.a more self-contained, so hopefully we can revisit this approach. Additionally, move the initialization code out of main() into a new init_git() function in its own file. Include this in libgit.a as well, so that external users can share our setup code without calling our main(). [1] https://lore.kernel.org/git/Yp+wjCPhqieTku3X@google.com/ [2] https://lore.kernel.org/git/20230517-unit-tests-v2-v2-1-21b5b60f4b32@google.com/ Signed-off-by: Josh Steadmon --- Makefile | 2 ++ common-exit.c | 26 ++++++++++++++++ common-init.c | 63 ++++++++++++++++++++++++++++++++++++++ common-init.h | 6 ++++ common-main.c | 83 ++------------------------------------------------- meson.build | 2 ++ 6 files changed, 101 insertions(+), 81 deletions(-) create mode 100644 common-exit.c create mode 100644 common-init.c create mode 100644 common-init.h diff --git a/Makefile b/Makefile index 97e8385b66..27e68ac039 100644 --- a/Makefile +++ b/Makefile @@ -981,6 +981,8 @@ LIB_OBJS += combine-diff.o LIB_OBJS += commit-graph.o LIB_OBJS += commit-reach.o LIB_OBJS += commit.o +LIB_OBJS += common-exit.o +LIB_OBJS += common-init.o LIB_OBJS += compat/nonblock.o LIB_OBJS += compat/obstack.o LIB_OBJS += compat/terminal.o diff --git a/common-exit.c b/common-exit.c new file mode 100644 index 0000000000..1aaa538be3 --- /dev/null +++ b/common-exit.c @@ -0,0 +1,26 @@ +#include "git-compat-util.h" +#include "trace2.h" + +static void check_bug_if_BUG(void) +{ + if (!bug_called_must_BUG) + return; + BUG("on exit(): had bug() call(s) in this process without explicit BUG_if_bug()"); +} + +/* We wrap exit() to call common_exit() in git-compat-util.h */ +int common_exit(const char *file, int line, int code) +{ + /* + * For non-POSIX systems: Take the lowest 8 bits of the "code" + * to e.g. turn -1 into 255. On a POSIX system this is + * redundant, see exit(3) and wait(2), but as it doesn't harm + * anything there we don't need to guard this with an "ifdef". + */ + code &= 0xff; + + check_bug_if_BUG(); + trace2_cmd_exit_fl(file, line, code); + + return code; +} diff --git a/common-init.c b/common-init.c new file mode 100644 index 0000000000..5cc73f058c --- /dev/null +++ b/common-init.c @@ -0,0 +1,63 @@ +#define USE_THE_REPOSITORY_VARIABLE + +#include "git-compat-util.h" +#include "common-init.h" +#include "exec-cmd.h" +#include "gettext.h" +#include "attr.h" +#include "repository.h" +#include "setup.h" +#include "strbuf.h" +#include "trace2.h" + +/* + * Many parts of Git have subprograms communicate via pipe, expect the + * upstream of a pipe to die with SIGPIPE when the downstream of a + * pipe does not need to read all that is written. Some third-party + * programs that ignore or block SIGPIPE for their own reason forget + * to restore SIGPIPE handling to the default before spawning Git and + * break this carefully orchestrated machinery. + * + * Restore the way SIGPIPE is handled to default, which is what we + * expect. + */ +static void restore_sigpipe_to_default(void) +{ + sigset_t unblock; + + sigemptyset(&unblock); + sigaddset(&unblock, SIGPIPE); + sigprocmask(SIG_UNBLOCK, &unblock, NULL); + signal(SIGPIPE, SIG_DFL); +} + +void init_git(const char **argv) +{ + struct strbuf tmp = STRBUF_INIT; + + trace2_initialize_clock(); + + /* + * Always open file descriptors 0/1/2 to avoid clobbering files + * in die(). It also avoids messing up when the pipes are dup'ed + * onto stdin/stdout/stderr in the child processes we spawn. + */ + sanitize_stdfds(); + restore_sigpipe_to_default(); + + git_resolve_executable_dir(argv[0]); + + setlocale(LC_CTYPE, ""); + git_setup_gettext(); + + initialize_repository(the_repository); + + attr_start(); + + trace2_initialize(); + trace2_cmd_start(argv); + trace2_collect_process_info(TRACE2_PROCESS_INFO_STARTUP); + + if (!strbuf_getcwd(&tmp)) + tmp_original_cwd = strbuf_detach(&tmp, NULL); +} diff --git a/common-init.h b/common-init.h new file mode 100644 index 0000000000..3e6db20cae --- /dev/null +++ b/common-init.h @@ -0,0 +1,6 @@ +#ifndef COMMON_INIT_H +#define COMMON_INIT_H + +void init_git(const char **argv); + +#endif /* COMMON_INIT_H */ diff --git a/common-main.c b/common-main.c index 8e68ac9e42..6b7ab077b0 100644 --- a/common-main.c +++ b/common-main.c @@ -1,92 +1,13 @@ -#define USE_THE_REPOSITORY_VARIABLE - #include "git-compat-util.h" -#include "exec-cmd.h" -#include "gettext.h" -#include "attr.h" -#include "repository.h" -#include "setup.h" -#include "strbuf.h" -#include "trace2.h" - -/* - * Many parts of Git have subprograms communicate via pipe, expect the - * upstream of a pipe to die with SIGPIPE when the downstream of a - * pipe does not need to read all that is written. Some third-party - * programs that ignore or block SIGPIPE for their own reason forget - * to restore SIGPIPE handling to the default before spawning Git and - * break this carefully orchestrated machinery. - * - * Restore the way SIGPIPE is handled to default, which is what we - * expect. - */ -static void restore_sigpipe_to_default(void) -{ - sigset_t unblock; - - sigemptyset(&unblock); - sigaddset(&unblock, SIGPIPE); - sigprocmask(SIG_UNBLOCK, &unblock, NULL); - signal(SIGPIPE, SIG_DFL); -} +#include "common-init.h" int main(int argc, const char **argv) { int result; - struct strbuf tmp = STRBUF_INIT; - - trace2_initialize_clock(); - - /* - * Always open file descriptors 0/1/2 to avoid clobbering files - * in die(). It also avoids messing up when the pipes are dup'ed - * onto stdin/stdout/stderr in the child processes we spawn. - */ - sanitize_stdfds(); - restore_sigpipe_to_default(); - - git_resolve_executable_dir(argv[0]); - - setlocale(LC_CTYPE, ""); - git_setup_gettext(); - - initialize_repository(the_repository); - - attr_start(); - - trace2_initialize(); - trace2_cmd_start(argv); - trace2_collect_process_info(TRACE2_PROCESS_INFO_STARTUP); - - if (!strbuf_getcwd(&tmp)) - tmp_original_cwd = strbuf_detach(&tmp, NULL); + init_git(argv); result = cmd_main(argc, argv); /* Not exit(3), but a wrapper calling our common_exit() */ exit(result); } - -static void check_bug_if_BUG(void) -{ - if (!bug_called_must_BUG) - return; - BUG("on exit(): had bug() call(s) in this process without explicit BUG_if_bug()"); -} - -/* We wrap exit() to call common_exit() in git-compat-util.h */ -int common_exit(const char *file, int line, int code) -{ - /* - * For non-POSIX systems: Take the lowest 8 bits of the "code" - * to e.g. turn -1 into 255. On a POSIX system this is - * redundant, see exit(3) and wait(2), but as it doesn't harm - * anything there we don't need to guard this with an "ifdef". - */ - code &= 0xff; - - check_bug_if_BUG(); - trace2_cmd_exit_fl(file, line, code); - - return code; -} diff --git a/meson.build b/meson.build index 0064eb64f5..e5ba28b47f 100644 --- a/meson.build +++ b/meson.build @@ -245,6 +245,8 @@ libgit_sources = [ 'commit-graph.c', 'commit-reach.c', 'commit.c', + 'common-exit.c', + 'common-init.c', 'compat/nonblock.c', 'compat/obstack.c', 'compat/terminal.c', From patchwork Tue Jan 28 22:01:38 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Josh Steadmon X-Patchwork-Id: 13953215 Received: from mail-pj1-f73.google.com (mail-pj1-f73.google.com [209.85.216.73]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 03638199E80 for ; Tue, 28 Jan 2025 22:01:46 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.73 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1738101709; cv=none; b=SUlbRJFm+f8s4B8k8aNf5naa8W1kIsHffEmYoLuxrx29p4jUppqOF37jGqjCX9Ks2BtvLU+VSnRqY8mieJQbpw+QUEw4DravVDd7xhdvt+qWxnDvpfVcUbdnmPHGMqsByMw5p6dP2wm0rfxvIcKkLgVceh0E3QYD1pjf/WOraN8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1738101709; c=relaxed/simple; bh=eNeB+tuLk2FzHqXYwTCMKBbzSInL1J6u84OI2BZD0S8=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=Wc5LZcQcGDVETrIyK+kFYqIxvokttj7Sqboe31YJ6tM/N+DV2FHHWFKahzB4cKindzL96oRcZ2CkvSa3J+lrC/zhOuhOFxGX4rPVBIXqcnpf+jCl/v2pk/dCvHG6c4B4wy5bIqfCU+Is74lxpd+VwsCrHKXc+z/X9LQutqyxiAo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--steadmon.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=19baYYLU; arc=none smtp.client-ip=209.85.216.73 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--steadmon.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="19baYYLU" Received: by mail-pj1-f73.google.com with SMTP id 98e67ed59e1d1-2ef80d30df1so11390690a91.1 for ; Tue, 28 Jan 2025 14:01:46 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1738101706; x=1738706506; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=zw9QD1gRHffuFJdR4ZUNdm8WM427E4xqRUBni93vz6k=; b=19baYYLUaXSAzcTn1CtP/n2qxBFCACUv4ngYeG2bzKlD+xOfQIEuBXEC0zdDre4Iaa ImXmZ4epNb19FHnqD62ZyqoxtDEirKUXIr1e0AE3Ag0gn64g4B4LtCnd1i9ENH1A9rw2 7g0JQbz3PAw/cVm1n3r/vBbAy+8XYB94ndq84xfDtar13wRNX51XRcm/IiT+6jh/H+TS b5wXm6o4/dbssWUUnHLOdOoOqy3PMjjDn8MLkBP/v8dQlSSenEtblk/FUUvX63NzLVqu dNDUPBIFUsJ4pnI+AlBApf73/nBK2ZvdpksYcNJV7/mD1h1GhRYOwf1+Zi6NO8uREvXP wSUg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1738101706; x=1738706506; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=zw9QD1gRHffuFJdR4ZUNdm8WM427E4xqRUBni93vz6k=; b=S0Szi+IXC9AJZusaBzGjUt3kiKoiaTUMd2701wO+wysXCg7fSHAyPtCzkioNebradP SKSLsSpd8055XlXd76HopF6+8G6zh1ubYifU4rTDs/D5QmXvez9xodTUYSfH4nUHdL23 SAQgjg1ewBtfDbDvbA4p0PCagVSIBvfFle18/DBSOBcpdJpbV4W/56SRC9Q9rwhRJnuI C5291uY6hIwFi20MoNPlmwb0KfzANfXv8OODc03oe9sZtJND6tNeWWCxCjLae3seSqeM x6IjbuAB+x4JUePQ3tVBCkz/hw6c9SzqpjobkWd2Brk2b2N7ROSTUXXV1Qmjp9R9+16w eO8w== X-Gm-Message-State: AOJu0YwfsCkRUNkRGVGRoxkPsiKegqD9sk+5xbrk/QjrjkF5hvs9K1is 1UeSj4GxYohAW2409n4771A5Vym08hpsAfKkcZ6ynT7Zr8GvU6UzV1SN0ScAq2IUbCKFRNDeOIx M5FRcWriowxWvrkHRosgODYu2Nr35B0zs0ZP8Fp5lplbqsRPL7lMJu6yYF+rwUKYc1NxMVDCMfP hutJShu1PUChtKhymncg5jN6BSvzw+TjDSDbvdoVE= X-Google-Smtp-Source: AGHT+IEFa28gEPlVczaKEK7lDKNsqemIQFBLcuZwd9nfuq22EhMYYxRqAf0pTtECw1C5CHh2sfoHMA70f4Mm/Q== X-Received: from pjb14.prod.google.com ([2002:a17:90b:2f0e:b0:2ea:931d:7ced]) (user=steadmon job=prod-delivery.src-stubby-dispatcher) by 2002:a17:90b:280c:b0:2ee:741c:e9f4 with SMTP id 98e67ed59e1d1-2f83abb8dd5mr997486a91.11.1738101706041; Tue, 28 Jan 2025 14:01:46 -0800 (PST) Date: Tue, 28 Jan 2025 14:01:38 -0800 In-Reply-To: Precedence: bulk X-Mailing-List: git@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: X-Mailer: git-send-email 2.48.1.262.g85cc9f2d1e-goog Message-ID: <3588a3c3fc76fd2a98b89e837dae07fd97fb88af.1738101256.git.steadmon@google.com> Subject: [PATCH v8 2/4] libgit-sys: introduce Rust wrapper for libgit.a From: Josh Steadmon To: git@vger.kernel.org Cc: calvinwan@google.com, nasamuffin@google.com, emrass@google.com, gitster@pobox.com, sandals@crustytoothpaste.net, ps@pks.im, phillip.wood123@gmail.com Introduce libgit-sys, a Rust wrapper crate that allows Rust code to call functions in libgit.a. This initial patch defines build rules and an interface that exposes user agent string getter functions as a proof of concept. This library can be tested with `cargo test`. In later commits, a higher-level library containing a more Rust-friendly interface will be added at `contrib/libgit-rs`. Symbols in libgit can collide with symbols from other libraries such as libgit2. We avoid this by first exposing library symbols in public_symbol_export.[ch]. These symbols are prepended with "libgit_" to avoid collisions and set to visible using a visibility pragma. In build.rs, Rust builds contrib/libgit-rs/libgit-sys/libgitpub.a, which also contains libgit.a and other dependent libraries, with -fvisibility=hidden to hide all symbols within those libraries that haven't been exposed with a visibility pragma. Co-authored-by: Kyle Lippincott Co-authored-by: Calvin Wan Signed-off-by: Calvin Wan Signed-off-by: Kyle Lippincott Signed-off-by: Josh Steadmon --- .gitignore | 1 + Makefile | 47 +++++++++++++++ contrib/libgit-sys/Cargo.lock | 69 +++++++++++++++++++++++ contrib/libgit-sys/Cargo.toml | 19 +++++++ contrib/libgit-sys/README.md | 4 ++ contrib/libgit-sys/build.rs | 35 ++++++++++++ contrib/libgit-sys/public_symbol_export.c | 23 ++++++++ contrib/libgit-sys/public_symbol_export.h | 8 +++ contrib/libgit-sys/src/lib.rs | 46 +++++++++++++++ t/Makefile | 10 ++++ 10 files changed, 262 insertions(+) create mode 100644 contrib/libgit-sys/Cargo.lock create mode 100644 contrib/libgit-sys/Cargo.toml create mode 100644 contrib/libgit-sys/README.md create mode 100644 contrib/libgit-sys/build.rs create mode 100644 contrib/libgit-sys/public_symbol_export.c create mode 100644 contrib/libgit-sys/public_symbol_export.h create mode 100644 contrib/libgit-sys/src/lib.rs diff --git a/.gitignore b/.gitignore index e82aa19df0..31d7e64287 100644 --- a/.gitignore +++ b/.gitignore @@ -250,3 +250,4 @@ Release/ /git.VC.db *.dSYM /contrib/buildsystems/out +/contrib/libgit-sys/target diff --git a/Makefile b/Makefile index 27e68ac039..f2c08df4cb 100644 --- a/Makefile +++ b/Makefile @@ -416,6 +416,9 @@ include shared.mak # Define LINK_FUZZ_PROGRAMS if you want `make all` to also build the fuzz test # programs in oss-fuzz/. # +# Define INCLUDE_LIBGIT_RS if you want `make all` and `make test` to build and +# test the Rust crate in contrib/libgit-sys. +# # === Optional library: libintl === # # Define NO_GETTEXT if you don't want Git output to be translated. @@ -657,6 +660,8 @@ CURL_CONFIG = curl-config GCOV = gcov STRIP = strip SPATCH = spatch +LD = ld +OBJCOPY = objcopy export TCL_PATH TCLTK_PATH @@ -2236,6 +2241,12 @@ ifdef FSMONITOR_OS_SETTINGS COMPAT_OBJS += compat/fsmonitor/fsm-path-utils-$(FSMONITOR_OS_SETTINGS).o endif +ifdef INCLUDE_LIBGIT_RS + # Enable symbol hiding in contrib/libgit-sys/libgitpub.a without making + # us rebuild the whole tree every time we run a Rust build. + BASIC_CFLAGS += -fvisibility=hidden +endif + ifeq ($(TCLTK_PATH),) NO_TCLTK = NoThanks endif @@ -2732,6 +2743,10 @@ OBJECTS += $(UNIT_TEST_OBJS) OBJECTS += $(CLAR_TEST_OBJS) OBJECTS += $(patsubst %,$(UNIT_TEST_DIR)/%.o,$(UNIT_TEST_PROGRAMS)) +ifdef INCLUDE_LIBGIT_RS + OBJECTS += contrib/libgit-sys/public_symbol_export.o +endif + ifndef NO_CURL OBJECTS += http.o http-walker.o remote-curl.o endif @@ -3726,6 +3741,10 @@ clean: profile-clean coverage-clean cocciclean $(RM) $(htmldocs).tar.gz $(manpages).tar.gz $(MAKE) -C Documentation/ clean $(RM) Documentation/GIT-EXCLUDED-PROGRAMS + $(RM) -r contrib/libgit-sys/target + $(RM) contrib/libgit-sys/partial_symbol_export.o + $(RM) contrib/libgit-sys/hidden_symbol_export.o + $(RM) contrib/libgit-sys/libgitpub.a ifndef NO_PERL $(RM) -r perl/build/ endif @@ -3887,3 +3906,31 @@ $(CLAR_TEST_PROG): $(UNIT_TEST_DIR)/clar.suite $(CLAR_TEST_OBJS) $(GITLIBS) GIT- build-unit-tests: $(UNIT_TEST_PROGS) $(CLAR_TEST_PROG) unit-tests: $(UNIT_TEST_PROGS) $(CLAR_TEST_PROG) t/helper/test-tool$X $(MAKE) -C t/ unit-tests + +.PHONY: libgit-sys +libgit-sys: + $(QUIET)(\ + cd contrib/libgit-sys && \ + cargo build \ + ) +ifdef INCLUDE_LIBGIT_RS +all:: libgit-sys +endif + +LIBGIT_PUB_OBJS = contrib/libgit-sys/public_symbol_export.o +LIBGIT_PUB_OBJS += libgit.a +LIBGIT_PUB_OBJS += reftable/libreftable.a +LIBGIT_PUB_OBJS += xdiff/lib.a + +LIBGIT_PARTIAL_EXPORT = contrib/libgit-sys/partial_symbol_export.o + +LIBGIT_HIDDEN_EXPORT = contrib/libgit-sys/hidden_symbol_export.o + +$(LIBGIT_PARTIAL_EXPORT): $(LIBGIT_PUB_OBJS) + $(LD) -r $^ -o $@ + +$(LIBGIT_HIDDEN_EXPORT): $(LIBGIT_PARTIAL_EXPORT) + $(OBJCOPY) --localize-hidden $^ $@ + +contrib/libgit-sys/libgitpub.a: $(LIBGIT_HIDDEN_EXPORT) + $(AR) $(ARFLAGS) $@ $^ diff --git a/contrib/libgit-sys/Cargo.lock b/contrib/libgit-sys/Cargo.lock new file mode 100644 index 0000000000..427a4c66b7 --- /dev/null +++ b/contrib/libgit-sys/Cargo.lock @@ -0,0 +1,69 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "cc" +version = "1.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57b6a275aa2903740dc87da01c62040406b8812552e97129a63ea8850a17c6e6" +dependencies = [ + "shlex", +] + +[[package]] +name = "libc" +version = "0.2.158" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" + +[[package]] +name = "libgit-sys" +version = "0.1.0" +dependencies = [ + "autocfg", + "libz-sys", + "make-cmd", +] + +[[package]] +name = "libz-sys" +version = "1.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2d16453e800a8cf6dd2fc3eb4bc99b786a9b90c663b8559a5b1a041bf89e472" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "make-cmd" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8ca8afbe8af1785e09636acb5a41e08a765f5f0340568716c18a8700ba3c0d3" + +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" diff --git a/contrib/libgit-sys/Cargo.toml b/contrib/libgit-sys/Cargo.toml new file mode 100644 index 0000000000..e0623022c3 --- /dev/null +++ b/contrib/libgit-sys/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "libgit-sys" +version = "0.1.0" +edition = "2021" +build = "build.rs" +links = "gitpub" +rust-version = "1.63" # TODO: Once we hit 1.84 or newer, we may want to remove Cargo.lock from + # version control. See https://lore.kernel.org/git/Z47jgK-oMjFRSslr@tapette.crustytoothpaste.net/ +description = "Native bindings to a portion of libgit" + +[lib] +path = "src/lib.rs" + +[dependencies] +libz-sys = "1.1.19" + +[build-dependencies] +autocfg = "1.4.0" +make-cmd = "0.1.0" diff --git a/contrib/libgit-sys/README.md b/contrib/libgit-sys/README.md new file mode 100644 index 0000000000..c061cfcaf5 --- /dev/null +++ b/contrib/libgit-sys/README.md @@ -0,0 +1,4 @@ +# libgit-sys + +A small proof-of-concept crate showing how to provide a Rust FFI to Git +internals. diff --git a/contrib/libgit-sys/build.rs b/contrib/libgit-sys/build.rs new file mode 100644 index 0000000000..3ffd80ad91 --- /dev/null +++ b/contrib/libgit-sys/build.rs @@ -0,0 +1,35 @@ +use std::env; +use std::path::PathBuf; + +pub fn main() -> std::io::Result<()> { + let ac = autocfg::new(); + ac.emit_has_path("std::ffi::c_char"); + + let crate_root = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap()); + let git_root = crate_root.join("../.."); + let dst = PathBuf::from(env::var_os("OUT_DIR").unwrap()); + + let make_output = make_cmd::gnu_make() + .env("DEVELOPER", "1") + .env_remove("PROFILE") + .current_dir(git_root.clone()) + .args([ + "INCLUDE_LIBGIT_RS=YesPlease", + "contrib/libgit-sys/libgitpub.a", + ]) + .output() + .expect("Make failed to run"); + if !make_output.status.success() { + panic!( + "Make failed:\n stdout = {}\n stderr = {}\n", + String::from_utf8(make_output.stdout).unwrap(), + String::from_utf8(make_output.stderr).unwrap() + ); + } + std::fs::copy(crate_root.join("libgitpub.a"), dst.join("libgitpub.a"))?; + println!("cargo:rustc-link-search=native={}", dst.display()); + println!("cargo:rustc-link-lib=gitpub"); + println!("cargo:rerun-if-changed={}", git_root.display()); + + Ok(()) +} diff --git a/contrib/libgit-sys/public_symbol_export.c b/contrib/libgit-sys/public_symbol_export.c new file mode 100644 index 0000000000..4c153c6f0d --- /dev/null +++ b/contrib/libgit-sys/public_symbol_export.c @@ -0,0 +1,23 @@ +/* + * Shim to publicly export Git symbols. These must be renamed so that the + * original symbols can be hidden. Renaming these with a "libgit_" prefix also + * avoids conflicts with other libraries such as libgit2. + */ + +#include "git-compat-util.h" +#include "contrib/libgit-sys/public_symbol_export.h" +#include "version.h" + +#pragma GCC visibility push(default) + +const char *libgit_user_agent(void) +{ + return git_user_agent(); +} + +const char *libgit_user_agent_sanitized(void) +{ + return git_user_agent_sanitized(); +} + +#pragma GCC visibility pop diff --git a/contrib/libgit-sys/public_symbol_export.h b/contrib/libgit-sys/public_symbol_export.h new file mode 100644 index 0000000000..a3372f93fa --- /dev/null +++ b/contrib/libgit-sys/public_symbol_export.h @@ -0,0 +1,8 @@ +#ifndef PUBLIC_SYMBOL_EXPORT_H +#define PUBLIC_SYMBOL_EXPORT_H + +const char *libgit_user_agent(void); + +const char *libgit_user_agent_sanitized(void); + +#endif /* PUBLIC_SYMBOL_EXPORT_H */ diff --git a/contrib/libgit-sys/src/lib.rs b/contrib/libgit-sys/src/lib.rs new file mode 100644 index 0000000000..d4853f3074 --- /dev/null +++ b/contrib/libgit-sys/src/lib.rs @@ -0,0 +1,46 @@ +#[cfg(has_std__ffi__c_char)] +use std::ffi::c_char; + +#[cfg(not(has_std__ffi__c_char))] +#[allow(non_camel_case_types)] +pub type c_char = i8; + +extern crate libz_sys; + +extern "C" { + pub fn libgit_user_agent() -> *const c_char; + pub fn libgit_user_agent_sanitized() -> *const c_char; +} + +#[cfg(test)] +mod tests { + use std::ffi::CStr; + + use super::*; + + #[test] + fn user_agent_starts_with_git() { + let c_str = unsafe { CStr::from_ptr(libgit_user_agent()) }; + let agent = c_str + .to_str() + .expect("User agent contains invalid UTF-8 data"); + assert!( + agent.starts_with("git/"), + r#"Expected user agent to start with "git/", got: {}"#, + agent + ); + } + + #[test] + fn sanitized_user_agent_starts_with_git() { + let c_str = unsafe { CStr::from_ptr(libgit_user_agent_sanitized()) }; + let agent = c_str + .to_str() + .expect("Sanitized user agent contains invalid UTF-8 data"); + assert!( + agent.starts_with("git/"), + r#"Expected user agent to start with "git/", got: {}"#, + agent + ); + } +} diff --git a/t/Makefile b/t/Makefile index daa5fcae86..53ba01c21b 100644 --- a/t/Makefile +++ b/t/Makefile @@ -177,3 +177,13 @@ perf: .PHONY: pre-clean $(T) aggregate-results clean valgrind perf \ check-chainlint clean-chainlint test-chainlint $(UNIT_TESTS) + +.PHONY: libgit-sys-test +libgit-sys-test: + $(QUIET)(\ + cd ../contrib/libgit-sys && \ + cargo test \ + ) +ifdef INCLUDE_LIBGIT_RS +all:: libgit-sys-test +endif From patchwork Tue Jan 28 22:01:39 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Josh Steadmon X-Patchwork-Id: 13953216 Received: from mail-pj1-f74.google.com (mail-pj1-f74.google.com [209.85.216.74]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8A48A19ADA2 for ; Tue, 28 Jan 2025 22:01:48 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.74 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1738101710; cv=none; b=ntRNsoNVHLu7alnR0goRrdekDajkohjbwmP4Cowu/2eW5TUk80m2h/qzcMVzr5f4ORDWCDQIOJOUCa0QzfnuIWLUfgHf8eRQDZnWvdYX4Qfdr1cpGEl/cOATr1E/uFhYVx2ODdsa46Kb0YpBVbIf54xoIDLev7f3a5Z0HazxZ+4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1738101710; c=relaxed/simple; bh=t/NpBUIaToMVT7OgZsBymt71cn9NkJdvdJqA34KDSJI=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=FPCdBCAHa7dZO1FTaimA4jjlvUL+pG7YEwEFojKJzLH6rsMNRcWUAaKGrZo54vf9xjmdqimgUFfsMvolrLPUoF9NJ7GHP4nhhuRkx6TqlUr4U8V4FfoX6lWjLDwXiAeekGe20vtWHn577xlMI3RhrlbzoFSN0dGpvL+1cGLSVnM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--steadmon.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=E83yKAu+; arc=none smtp.client-ip=209.85.216.74 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--steadmon.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="E83yKAu+" Received: by mail-pj1-f74.google.com with SMTP id 98e67ed59e1d1-2efa74481fdso12385607a91.1 for ; Tue, 28 Jan 2025 14:01:48 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1738101708; x=1738706508; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=DzAFp4YAtbOU6jl9VfN/7dfiX3slc8SYxdEGxReh9zY=; b=E83yKAu+I6UF+2ek9w/a3+zJdE02k6bk2oydHgvEoHKuOofY307krW8dUcq9apAjCe 7mA1V27fyHU8kVoQbne7CpEZJR5+AaNSluN8bjUZ9QcLeVD9uNC3O1fljBRt/TYldkY6 rblSVB6nTcYzuqfOywSYVOS+dIXmxkcV1mOGIJuE81RzQg6FB7u8ooTbE1ODK1m7nQgK upS4VeYZQClQt2D9Nn4tWWZBWDXc6zlVgSuQXTbp0TM1ypEJNCnH2+4/2E5QLnjHsgvQ KEyqn7ZrCHfw3nt17piz6slMF40GkjHYK3R0rmlHzGEEyVxmpOBFDCqFkmo14lUjg1G1 O7yg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1738101708; x=1738706508; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=DzAFp4YAtbOU6jl9VfN/7dfiX3slc8SYxdEGxReh9zY=; b=uwp+7K7FPmQab3kyRamXE2RzgZGvSKMeMr0KiL5e3tI1NaxvmnNJljGQ6apLKHS3Gr fYsplD5lmY4JQvEtHaAi7KNjiL7DwqI7kV0Bey5e0YtnVdyzn3koV2YHTHFGtC6QQiXQ K0TgfjQLzCwC5CqPa/PapHArXHIrQPKcxpBCGxQ6MGeyXJv9eP7hBFdKHy3F6hJZtuwi eQEuQeS3YDJSxfGEMCVMPF0Fbcowq5JZpqAInulMV1Uz6GWQoNRP3fUBgFjlAomw8XeV LAEdtZKOTp5y91c2HBmwwjLsTr/kgLjRWL+glvn1a83A67uSrnlK/omENQs/d7r5jTvj 3n6g== X-Gm-Message-State: AOJu0Yx13nwYXi0lZguzypePbw5JojuiGB+IITa+95chaMx31sIeLihM L7w22iLdgYuHU04TlB3yDZfjs8veRGbRSl1lgyIQ0WYnbn7BZzweO71lVQnTRCfs0xcuFKULZTj spkhxB2KUyRE5/dYr33yLIOisZMJq9bdYILfgo6ZMyVeiCmLSlByMo2AYVnERvMgThakfknNtyw Yj6H0upt8EA1/m+x4T9RcGpUxXYzKacpS6Ye6deiQ= X-Google-Smtp-Source: AGHT+IGtnz7evXRiE4wnwAwMCDg0k/YiEXUmRMB9XTED+JfatnTRdFaH+YXLCWNaflcQzxOnMWyVl02GW57MOQ== X-Received: from pjvf7.prod.google.com ([2002:a17:90a:da87:b0:2ef:95f4:4619]) (user=steadmon job=prod-delivery.src-stubby-dispatcher) by 2002:a17:90b:4b88:b0:2f4:9e8b:6aad with SMTP id 98e67ed59e1d1-2f83aa804cfmr1285329a91.0.1738101707683; Tue, 28 Jan 2025 14:01:47 -0800 (PST) Date: Tue, 28 Jan 2025 14:01:39 -0800 In-Reply-To: Precedence: bulk X-Mailing-List: git@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: X-Mailer: git-send-email 2.48.1.262.g85cc9f2d1e-goog Message-ID: Subject: [PATCH v8 3/4] libgit-sys: also export some config_set functions From: Josh Steadmon To: git@vger.kernel.org Cc: calvinwan@google.com, nasamuffin@google.com, emrass@google.com, gitster@pobox.com, sandals@crustytoothpaste.net, ps@pks.im, phillip.wood123@gmail.com In preparation for implementing a higher-level Rust API for accessing Git configs, export some of the upstream configset API via libgitpub and libgit-sys. Since this will be exercised as part of the higher-level API in the next commit, no tests have been added for libgit-sys. While we're at it, add git_configset_alloc() and git_configset_free() functions in libgitpub so that callers can manage config_set structs on the heap. This also allows non-C external consumers to treat config_sets as opaque structs. Co-authored-by: Calvin Wan Signed-off-by: Calvin Wan Signed-off-by: Josh Steadmon --- contrib/libgit-sys/public_symbol_export.c | 36 +++++++++++++++++++++++ contrib/libgit-sys/public_symbol_export.h | 10 +++++++ contrib/libgit-sys/src/lib.rs | 31 ++++++++++++++++++- 3 files changed, 76 insertions(+), 1 deletion(-) diff --git a/contrib/libgit-sys/public_symbol_export.c b/contrib/libgit-sys/public_symbol_export.c index 4c153c6f0d..dbb7948104 100644 --- a/contrib/libgit-sys/public_symbol_export.c +++ b/contrib/libgit-sys/public_symbol_export.c @@ -5,11 +5,47 @@ */ #include "git-compat-util.h" +#include "config.h" #include "contrib/libgit-sys/public_symbol_export.h" #include "version.h" #pragma GCC visibility push(default) +struct libgit_config_set { + struct config_set cs; +}; + +struct libgit_config_set *libgit_configset_alloc(void) +{ + struct libgit_config_set *cs = + xmalloc(sizeof(struct libgit_config_set)); + git_configset_init(&cs->cs); + return cs; +} + +void libgit_configset_free(struct libgit_config_set *cs) +{ + git_configset_clear(&cs->cs); + free(&cs->cs); +} + +int libgit_configset_add_file(struct libgit_config_set *cs, const char *filename) +{ + return git_configset_add_file(&cs->cs, filename); +} + +int libgit_configset_get_int(struct libgit_config_set *cs, const char *key, + int *dest) +{ + return git_configset_get_int(&cs->cs, key, dest); +} + +int libgit_configset_get_string(struct libgit_config_set *cs, const char *key, + char **dest) +{ + return git_configset_get_string(&cs->cs, key, dest); +} + const char *libgit_user_agent(void) { return git_user_agent(); diff --git a/contrib/libgit-sys/public_symbol_export.h b/contrib/libgit-sys/public_symbol_export.h index a3372f93fa..701db92d53 100644 --- a/contrib/libgit-sys/public_symbol_export.h +++ b/contrib/libgit-sys/public_symbol_export.h @@ -1,6 +1,16 @@ #ifndef PUBLIC_SYMBOL_EXPORT_H #define PUBLIC_SYMBOL_EXPORT_H +struct libgit_config_set *libgit_configset_alloc(void); + +void libgit_configset_free(struct libgit_config_set *cs); + +int libgit_configset_add_file(struct libgit_config_set *cs, const char *filename); + +int libgit_configset_get_int(struct libgit_config_set *cs, const char *key, int *dest); + +int libgit_configset_get_string(struct libgit_config_set *cs, const char *key, char **dest); + const char *libgit_user_agent(void); const char *libgit_user_agent_sanitized(void); diff --git a/contrib/libgit-sys/src/lib.rs b/contrib/libgit-sys/src/lib.rs index d4853f3074..dadb4e5f40 100644 --- a/contrib/libgit-sys/src/lib.rs +++ b/contrib/libgit-sys/src/lib.rs @@ -1,15 +1,44 @@ #[cfg(has_std__ffi__c_char)] -use std::ffi::c_char; +use std::ffi::{c_char, c_int}; #[cfg(not(has_std__ffi__c_char))] #[allow(non_camel_case_types)] pub type c_char = i8; +#[cfg(not(has_std__ffi__c_char))] +#[allow(non_camel_case_types)] +pub type c_int = i32; + extern crate libz_sys; +#[allow(non_camel_case_types)] +#[repr(C)] +pub struct libgit_config_set { + _data: [u8; 0], + _marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>, +} + extern "C" { pub fn libgit_user_agent() -> *const c_char; pub fn libgit_user_agent_sanitized() -> *const c_char; + + pub fn libgit_configset_alloc() -> *mut libgit_config_set; + pub fn libgit_configset_free(cs: *mut libgit_config_set); + + pub fn libgit_configset_add_file(cs: *mut libgit_config_set, filename: *const c_char) -> c_int; + + pub fn libgit_configset_get_int( + cs: *mut libgit_config_set, + key: *const c_char, + int: *mut c_int, + ) -> c_int; + + pub fn libgit_configset_get_string( + cs: *mut libgit_config_set, + key: *const c_char, + dest: *mut *mut c_char, + ) -> c_int; + } #[cfg(test)] From patchwork Tue Jan 28 22:01:40 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Josh Steadmon X-Patchwork-Id: 13953217 Received: from mail-oa1-f74.google.com (mail-oa1-f74.google.com [209.85.160.74]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 405CB1DFD95 for ; Tue, 28 Jan 2025 22:01:50 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.160.74 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1738101712; cv=none; b=NeuHJoiH3ZQ+5diVJ+SDGDwnicCb2NCMn3bgApIXOV5NbN4WOarUTiJtxhCRe6NLm4LDC1E6bCRDhOWDG+yD9lcNDknQrOrImDoJXiTWIXe76zRRdy/DSMBbTPTwEH2xZiMFxXimqlMsm2JEErD2Ai+Z8Qw6cz0mJQ/hx/dkf90= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1738101712; c=relaxed/simple; bh=p2HJuTxM42BBE9RV+vcPaIdFnhXcuNgrXIEIkkPT53M=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=dUNmAMAV59WUbGq2ODkOdq1F71XUNUmmEp1BNDoSR3eF4Hdi+e+UlFHg1hrxMzMyqzV4HJwOvSnFFQbPYywfAtuNiWU1N22VCiEl9ak4sBrQFiDito1ChpeFsBXXvRuOAuXiujR5K/nt8rqKN8T3hdaYYFm7amWFDRF3JNvNc9Q= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--steadmon.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=H01Jt/84; arc=none smtp.client-ip=209.85.160.74 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--steadmon.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="H01Jt/84" Received: by mail-oa1-f74.google.com with SMTP id 586e51a60fabf-2b268f6e85eso4335407fac.2 for ; Tue, 28 Jan 2025 14:01:50 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1738101709; x=1738706509; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=sfzi7vpvvL3sAOj7/y+ktcdyvKqi73B+gJIE5IQt3Ro=; b=H01Jt/841Y3rI1+5b/975hAjWWg3oTbRie699ds/QnPq0X7sjyRYxSrPtxoyYP+r/U TEy22JDNdXMsmnEKNdJrr6mz66FFopvWuUStikP0l+JoToZapVwwN42Q90Hisnu8t/1Q 9y2914wQaCBAwNWq8hnCR/H4c7ZDEVHUBksKkdXKj9wlhThKrBXCF5ZJ7OXBz7ealERZ jOxjPK+zEOkWxUg4O+QTZvo2khPwxuX8T2DWbmuLiiNQVQDkTnm7XFEaK1kMegNq4lrw hjuVo40PNhMABz8J3VmQzkIaPYVDFma4nSpm7qcaQcBz26o+2dy0BIRdYj8lr16Jdpvg 6mHg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1738101709; x=1738706509; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=sfzi7vpvvL3sAOj7/y+ktcdyvKqi73B+gJIE5IQt3Ro=; b=uUYELpSWQK+weGn/GOMKaWRAI4fE1Z8oMolntbbso4EBuxpcgP1PhyoT0Lm0Fl9PZO mjgJiLZnFVYZmupt0NhSaZDn81qvjJnrYW+R9MVrhWDgY5ImShRFugCrUQJXebkMp5ku klqmlO8InkCB80Hk/X+k/4PhV/EYqpdAX0MtBX7PGN38awR4+8+aKD95IqZgr1o5IvTC l18tVmEytqk7gGIiCkDc9K3RT4aafrz9PPDom3przJo/QMVlu0eEoK0sIt0ARuiEEPUq ti+E+WimqTcH7YXapbRmWkg4bKZTik7r2aXWMNZxZ2EQi+Y3fxEHBSd8hHRoPhxnM3Tr Ms0w== X-Gm-Message-State: AOJu0Yw/fqGjiKQCwjSfxEEqh2ot3rjSOQdFleHpc9nb8CuVdFVYfu6Y vJnLFN9AMlxHqTGlBLWbE5FghSvFWy40DcYEy6Fzelfg29PcJNPgOyw47/+lCadn4lHBgX2Jmq+ 1pQdGf5dCUdXaYeBJO+74lPVPmDSpo9Ntj4CgUGLFiDsq20j78sSTMdvoN5WlvZvcDNdRObuE4H /ub41ml8lugRQ33Z/82i1eEiVp6bpshK6YQLrgJSY= X-Google-Smtp-Source: AGHT+IFuW25WYuT6r7jB6EvtXDsiE2/5et8c4hySFIi7MIitPHUPo/cFRjGffXPYRVvunNPF5PfHn380SPfakA== X-Received: from oabmk6.prod.google.com ([2002:a05:6870:d06:b0:288:e08d:60c9]) (user=steadmon job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6871:538e:b0:297:2719:deb6 with SMTP id 586e51a60fabf-2b32f0997bamr525407fac.1.1738101709313; Tue, 28 Jan 2025 14:01:49 -0800 (PST) Date: Tue, 28 Jan 2025 14:01:40 -0800 In-Reply-To: Precedence: bulk X-Mailing-List: git@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: X-Mailer: git-send-email 2.48.1.262.g85cc9f2d1e-goog Message-ID: Subject: [PATCH v8 4/4] libgit: add higher-level libgit crate From: Josh Steadmon To: git@vger.kernel.org Cc: calvinwan@google.com, nasamuffin@google.com, emrass@google.com, gitster@pobox.com, sandals@crustytoothpaste.net, ps@pks.im, phillip.wood123@gmail.com From: Calvin Wan The C functions exported by libgit-sys do not provide an idiomatic Rust interface. To make it easier to use these functions via Rust, add a higher-level "libgit" crate, that wraps the lower-level configset API with an interface that is more Rust-y. This combination of $X and $X-sys crates is a common pattern for FFI in Rust, as documented in "The Cargo Book" [1]. [1] https://doc.rust-lang.org/cargo/reference/build-scripts.html#-sys-packages Co-authored-by: Josh Steadmon Signed-off-by: Josh Steadmon Signed-off-by: Calvin Wan --- .gitignore | 1 + Makefile | 12 ++-- contrib/libgit-rs/Cargo.lock | 77 +++++++++++++++++++++ contrib/libgit-rs/Cargo.toml | 17 +++++ contrib/libgit-rs/README.md | 13 ++++ contrib/libgit-rs/build.rs | 4 ++ contrib/libgit-rs/src/config.rs | 106 +++++++++++++++++++++++++++++ contrib/libgit-rs/src/lib.rs | 1 + contrib/libgit-rs/testdata/config1 | 2 + contrib/libgit-rs/testdata/config2 | 2 + contrib/libgit-rs/testdata/config3 | 2 + contrib/libgit-sys/src/lib.rs | 4 ++ t/Makefile | 9 ++- 13 files changed, 242 insertions(+), 8 deletions(-) create mode 100644 contrib/libgit-rs/Cargo.lock create mode 100644 contrib/libgit-rs/Cargo.toml create mode 100644 contrib/libgit-rs/README.md create mode 100644 contrib/libgit-rs/build.rs create mode 100644 contrib/libgit-rs/src/config.rs create mode 100644 contrib/libgit-rs/src/lib.rs create mode 100644 contrib/libgit-rs/testdata/config1 create mode 100644 contrib/libgit-rs/testdata/config2 create mode 100644 contrib/libgit-rs/testdata/config3 diff --git a/.gitignore b/.gitignore index 31d7e64287..acdd8ce7c7 100644 --- a/.gitignore +++ b/.gitignore @@ -250,4 +250,5 @@ Release/ /git.VC.db *.dSYM /contrib/buildsystems/out +/contrib/libgit-rs/target /contrib/libgit-sys/target diff --git a/Makefile b/Makefile index f2c08df4cb..f883d310aa 100644 --- a/Makefile +++ b/Makefile @@ -417,7 +417,7 @@ include shared.mak # programs in oss-fuzz/. # # Define INCLUDE_LIBGIT_RS if you want `make all` and `make test` to build and -# test the Rust crate in contrib/libgit-sys. +# test the Rust crates in contrib/libgit-sys and contrib/libgit-rs. # # === Optional library: libintl === # @@ -3741,7 +3741,7 @@ clean: profile-clean coverage-clean cocciclean $(RM) $(htmldocs).tar.gz $(manpages).tar.gz $(MAKE) -C Documentation/ clean $(RM) Documentation/GIT-EXCLUDED-PROGRAMS - $(RM) -r contrib/libgit-sys/target + $(RM) -r contrib/libgit-sys/target contrib/libgit-rs/target $(RM) contrib/libgit-sys/partial_symbol_export.o $(RM) contrib/libgit-sys/hidden_symbol_export.o $(RM) contrib/libgit-sys/libgitpub.a @@ -3907,14 +3907,14 @@ build-unit-tests: $(UNIT_TEST_PROGS) $(CLAR_TEST_PROG) unit-tests: $(UNIT_TEST_PROGS) $(CLAR_TEST_PROG) t/helper/test-tool$X $(MAKE) -C t/ unit-tests -.PHONY: libgit-sys -libgit-sys: +.PHONY: libgit-sys libgit-rs +libgit-sys libgit-rs: $(QUIET)(\ - cd contrib/libgit-sys && \ + cd contrib/$@ && \ cargo build \ ) ifdef INCLUDE_LIBGIT_RS -all:: libgit-sys +all:: libgit-sys libgit-rs endif LIBGIT_PUB_OBJS = contrib/libgit-sys/public_symbol_export.o diff --git a/contrib/libgit-rs/Cargo.lock b/contrib/libgit-rs/Cargo.lock new file mode 100644 index 0000000000..a30c7c8d33 --- /dev/null +++ b/contrib/libgit-rs/Cargo.lock @@ -0,0 +1,77 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "cc" +version = "1.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57b6a275aa2903740dc87da01c62040406b8812552e97129a63ea8850a17c6e6" +dependencies = [ + "shlex", +] + +[[package]] +name = "libc" +version = "0.2.158" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" + +[[package]] +name = "libgit" +version = "0.1.0" +dependencies = [ + "autocfg", + "libgit-sys", +] + +[[package]] +name = "libgit-sys" +version = "0.1.0" +dependencies = [ + "autocfg", + "libz-sys", + "make-cmd", +] + +[[package]] +name = "libz-sys" +version = "1.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2d16453e800a8cf6dd2fc3eb4bc99b786a9b90c663b8559a5b1a041bf89e472" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "make-cmd" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8ca8afbe8af1785e09636acb5a41e08a765f5f0340568716c18a8700ba3c0d3" + +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" diff --git a/contrib/libgit-rs/Cargo.toml b/contrib/libgit-rs/Cargo.toml new file mode 100644 index 0000000000..c3289e69db --- /dev/null +++ b/contrib/libgit-rs/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "libgit" +version = "0.1.0" +edition = "2021" +build = "build.rs" +rust-version = "1.63" # TODO: Once we hit 1.84 or newer, we may want to remove Cargo.lock from + # version control. See https://lore.kernel.org/git/Z47jgK-oMjFRSslr@tapette.crustytoothpaste.net/ + + +[lib] +path = "src/lib.rs" + +[dependencies] +libgit-sys = { version = "0.1.0", path = "../libgit-sys" } + +[build-dependencies] +autocfg = "1.4.0" diff --git a/contrib/libgit-rs/README.md b/contrib/libgit-rs/README.md new file mode 100644 index 0000000000..ff945e1ce2 --- /dev/null +++ b/contrib/libgit-rs/README.md @@ -0,0 +1,13 @@ +# libgit-rs + +Proof-of-concept Git bindings for Rust. + +```toml +[dependencies] +libgit = "0.1.0" +``` + +## Rust version requirements + +libgit-rs should support Rust versions at least as old as the version included +in Debian stable (currently 1.63). diff --git a/contrib/libgit-rs/build.rs b/contrib/libgit-rs/build.rs new file mode 100644 index 0000000000..f8bd01a690 --- /dev/null +++ b/contrib/libgit-rs/build.rs @@ -0,0 +1,4 @@ +pub fn main() { + let ac = autocfg::new(); + ac.emit_has_path("std::ffi::c_char"); +} diff --git a/contrib/libgit-rs/src/config.rs b/contrib/libgit-rs/src/config.rs new file mode 100644 index 0000000000..6bf04845c8 --- /dev/null +++ b/contrib/libgit-rs/src/config.rs @@ -0,0 +1,106 @@ +use std::ffi::{c_void, CStr, CString}; +use std::path::Path; + +#[cfg(has_std__ffi__c_char)] +use std::ffi::{c_char, c_int}; + +#[cfg(not(has_std__ffi__c_char))] +#[allow(non_camel_case_types)] +type c_char = i8; + +#[cfg(not(has_std__ffi__c_char))] +#[allow(non_camel_case_types)] +type c_int = i32; + +use libgit_sys::*; + +/// A ConfigSet is an in-memory cache for config-like files such as `.gitmodules` or `.gitconfig`. +/// It does not support all config directives; notably, it will not process `include` or +/// `includeIf` directives (but it will store them so that callers can choose whether and how to +/// handle them). +pub struct ConfigSet(*mut libgit_config_set); +impl ConfigSet { + /// Allocate a new ConfigSet + pub fn new() -> Self { + unsafe { ConfigSet(libgit_configset_alloc()) } + } + + /// Load the given files into the ConfigSet; conflicting directives in later files will + /// override those given in earlier files. + pub fn add_files(&mut self, files: &[&Path]) { + for file in files { + let pstr = file.to_str().expect("Invalid UTF-8"); + let rs = CString::new(pstr).expect("Couldn't convert to CString"); + unsafe { + libgit_configset_add_file(self.0, rs.as_ptr()); + } + } + } + + /// Load the value for the given key and attempt to parse it as an i32. Dies with a fatal error + /// if the value cannot be parsed. Returns None if the key is not present. + pub fn get_int(&mut self, key: &str) -> Option { + let key = CString::new(key).expect("Couldn't convert to CString"); + let mut val: c_int = 0; + unsafe { + if libgit_configset_get_int(self.0, key.as_ptr(), &mut val as *mut c_int) != 0 { + return None; + } + } + + Some(val.into()) + } + + /// Clones the value for the given key. Dies with a fatal error if the value cannot be + /// converted to a String. Returns None if the key is not present. + pub fn get_string(&mut self, key: &str) -> Option { + let key = CString::new(key).expect("Couldn't convert key to CString"); + let mut val: *mut c_char = std::ptr::null_mut(); + unsafe { + if libgit_configset_get_string(self.0, key.as_ptr(), &mut val as *mut *mut c_char) != 0 + { + return None; + } + let borrowed_str = CStr::from_ptr(val); + let owned_str = + String::from(borrowed_str.to_str().expect("Couldn't convert val to str")); + free(val as *mut c_void); // Free the xstrdup()ed pointer from the C side + Some(owned_str) + } + } +} + +impl Default for ConfigSet { + fn default() -> Self { + Self::new() + } +} + +impl Drop for ConfigSet { + fn drop(&mut self) { + unsafe { + libgit_configset_free(self.0); + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn load_configs_via_configset() { + let mut cs = ConfigSet::new(); + cs.add_files(&[ + Path::new("testdata/config1"), + Path::new("testdata/config2"), + Path::new("testdata/config3"), + ]); + // ConfigSet retrieves correct value + assert_eq!(cs.get_int("trace2.eventTarget"), Some(1)); + // ConfigSet respects last config value set + assert_eq!(cs.get_int("trace2.eventNesting"), Some(3)); + // ConfigSet returns None for missing key + assert_eq!(cs.get_string("foo.bar"), None); + } +} diff --git a/contrib/libgit-rs/src/lib.rs b/contrib/libgit-rs/src/lib.rs new file mode 100644 index 0000000000..ef68c36943 --- /dev/null +++ b/contrib/libgit-rs/src/lib.rs @@ -0,0 +1 @@ +pub mod config; diff --git a/contrib/libgit-rs/testdata/config1 b/contrib/libgit-rs/testdata/config1 new file mode 100644 index 0000000000..4e9a9d25d1 --- /dev/null +++ b/contrib/libgit-rs/testdata/config1 @@ -0,0 +1,2 @@ +[trace2] + eventNesting = 1 diff --git a/contrib/libgit-rs/testdata/config2 b/contrib/libgit-rs/testdata/config2 new file mode 100644 index 0000000000..b8d1eca423 --- /dev/null +++ b/contrib/libgit-rs/testdata/config2 @@ -0,0 +1,2 @@ +[trace2] + eventTarget = 1 diff --git a/contrib/libgit-rs/testdata/config3 b/contrib/libgit-rs/testdata/config3 new file mode 100644 index 0000000000..ca7b9a7c38 --- /dev/null +++ b/contrib/libgit-rs/testdata/config3 @@ -0,0 +1,2 @@ +[trace2] + eventNesting = 3 diff --git a/contrib/libgit-sys/src/lib.rs b/contrib/libgit-sys/src/lib.rs index dadb4e5f40..4bfc650450 100644 --- a/contrib/libgit-sys/src/lib.rs +++ b/contrib/libgit-sys/src/lib.rs @@ -1,3 +1,5 @@ +use std::ffi::c_void; + #[cfg(has_std__ffi__c_char)] use std::ffi::{c_char, c_int}; @@ -19,6 +21,8 @@ pub struct libgit_config_set { } extern "C" { + pub fn free(ptr: *mut c_void); + pub fn libgit_user_agent() -> *const c_char; pub fn libgit_user_agent_sanitized() -> *const c_char; diff --git a/t/Makefile b/t/Makefile index 53ba01c21b..2994eb5fa9 100644 --- a/t/Makefile +++ b/t/Makefile @@ -178,12 +178,17 @@ perf: .PHONY: pre-clean $(T) aggregate-results clean valgrind perf \ check-chainlint clean-chainlint test-chainlint $(UNIT_TESTS) -.PHONY: libgit-sys-test +.PHONY: libgit-sys-test libgit-rs-test libgit-sys-test: $(QUIET)(\ cd ../contrib/libgit-sys && \ cargo test \ ) +libgit-rs-test: + $(QUIET)(\ + cd ../contrib/libgit-rs && \ + cargo test \ + ) ifdef INCLUDE_LIBGIT_RS -all:: libgit-sys-test +all:: libgit-sys-test libgit-rs-test endif