From patchwork Tue Jan 28 00:19:02 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Josh Steadmon X-Patchwork-Id: 13951950 Received: from mail-pl1-f201.google.com (mail-pl1-f201.google.com [209.85.214.201]) (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 E9304567D for ; Tue, 28 Jan 2025 00:19:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1738023552; cv=none; b=piYAZoEBqNWxTeuGn70G4LYt6E3CPUmN7aR48rGRbp+//h/RmPSfniUPkr7vUVTp9/sekTT6un1Sda9Xw2GyAqUJOmErSa1iEKQ60SurTUrnbC87DiAgpRPp30NrgYEPp7yaDg6y6H7NW8EhitYkUXQvtoXPxuvmSZI8OkMqliU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1738023552; c=relaxed/simple; bh=CcHuSnvH00XLWG0FIpQ+TgQs3ih943r9TX3YnBfIGxA=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=dchbIqXswioy9ALdOJ2b8hIZzuAjS7NWDgz/7BYcWL3zP86PPTy0LTYZSYXIhERXa4yX5R0Pvknd21+QhpAJPsCJBDPt8+THVMXjdrTrlpxZ58pb9AjYDD/KRNZgcoipeby2zhUxbwRpp+RVo4TAqyVSI5hfEpmEjhF8ritmfrg= 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=H0S41fOo; arc=none smtp.client-ip=209.85.214.201 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="H0S41fOo" Received: by mail-pl1-f201.google.com with SMTP id d9443c01a7336-216430a88b0so95944635ad.0 for ; Mon, 27 Jan 2025 16:19:10 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1738023550; x=1738628350; 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=O1DZ8akkSqDYmVl2WfQuyLcEPNEXg7FUJybNwNLtAzA=; b=H0S41fOoUpTP2NPBz598+NWpTWHCoHufOHE7UZqXsICUYu0GWK/8WBOXwC/aCzbVsj AhW2nLDGMSmrrZFMflZhy+99SNIQumhPihQVDd7shF8J0x2y/+QsUz+PzKOdJ48aGrkD 5XUcDJWbAO33zjCRexVlhrgpaqFhN2e2owCDRUKHt1ZxIorSXr59UZEMybI5bsvr31bX tBMvkoIFPmTJJ4NIi55ctl05ziWZTRB+B6vaJR2v7mIwZ3+A+HiC+JefTmCOV5HlDtyf Ze4ELnJjnSeUvhgOUMXOAUJ7bCaB1qArxS6TkJjflJffkSAwnJejfRQTfl/thV/j+hPA N1EA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1738023550; x=1738628350; 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=O1DZ8akkSqDYmVl2WfQuyLcEPNEXg7FUJybNwNLtAzA=; b=pYvfOT6BSToQBgg6l5KlHoiejfsNddUuZc2YAN9ORWILCxgH2HAEkddh8JWRp5UkmC 1cfosEX+NlfQD9dq/AReX8Cd4f3yVTDJ4ee0K6//hu5iYr1O/hMG/oH/zY5BKuRQ4wcW +46ks4csbDNC1HX/O/t411tIb2G2sEOwzoi3HpKXiSEuyohPDFdGxZlw6fYwYxVDbmAb RkG8VhxUGNnbwwoVb57ZJO3gUcPVsrjqZLjSnPGF6KmG2pgkAiXDhe/jbl9MMmS/gdtj LcaL5UVLEpzU2IxNEpD5G3N3tzvGB+MjMKK9ZQehueVC/bpJftrQXKzSFjGQemZ/UqyH 4stQ== X-Gm-Message-State: AOJu0Yxj7M/C3WwIP4oXtZoi0SjvxjNauNeubjnE1PK4sUtqLQkg8KVo ZBE+wbitr+XR8BcO7UkmupX+19iVRRie1BVHyxNGa3oM1wshd0ap18JXAjytuSHAKVSEGmkmPLh uE9QvaeeM8XYRKU5UVuglhB+4XUBC9JprdEkG8+dlrRnTDX5IiadtZoNa/Yk6y47wvfCiu+rXhe AWP0j0ylBOsReYVlA7RnVYe4ITSjxcS2IbFH82BaE= X-Google-Smtp-Source: AGHT+IHqzH8WDwCKh6tgIXhYTvF7e+LgmoftkwMbQr/IvHyvRh/DOBJPFRo3bIGrXGpwF6ha0IQ6Z1akV/38/Q== X-Received: from plblm12.prod.google.com ([2002:a17:903:298c:b0:212:4fad:1dc1]) (user=steadmon job=prod-delivery.src-stubby-dispatcher) by 2002:a17:903:40cb:b0:215:a60d:bcc9 with SMTP id d9443c01a7336-21c351bd3bamr641169005ad.2.1738023550018; Mon, 27 Jan 2025 16:19:10 -0800 (PST) Date: Mon, 27 Jan 2025 16:19:02 -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 v7 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 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/ Helped-by: Junio C Hamano 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 00:19:03 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Josh Steadmon X-Patchwork-Id: 13951951 Received: from mail-pl1-f201.google.com (mail-pl1-f201.google.com [209.85.214.201]) (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 920BB4C8E for ; Tue, 28 Jan 2025 00:19:12 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1738023554; cv=none; b=HkJEK8hu9r8aguaSGis26ObUttTw5tHYxItZ0yUzaWSFc0imvL3bb86CWEph//KXjvHbVxHYISf/A+1+Bm2Ocx8VRbVwa0OAvEpdPQNR0BsWmzTHMY9pL7VDgRXrssw1tBwxBs+KUyFufTQKVpMNwMpSKWXlXTVRM8TeOWkeYyA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1738023554; c=relaxed/simple; bh=Hjn0sUqM3eS2WRGHajrGTpbQyzCbMEzn3aM3q6Y+Jfc=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=OB8B0dZiNh+rXTmtDvUs9fo/KTc1g4j5/kFxjRA9iD7u6ObdQlgjipjUQ8oqbYhQY0LXpztgF75HEzaHVcsuLitzUNMdzh88cKO8KTSPp/H+Ug+QwDWcdbyORG5bSyLGVxZYEw/hT2FVY8BesB4qRgeJCtGSSVMgP1SyxDYQf2U= 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=vsFNPTJY; arc=none smtp.client-ip=209.85.214.201 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="vsFNPTJY" Received: by mail-pl1-f201.google.com with SMTP id d9443c01a7336-2162259a5dcso156748825ad.3 for ; Mon, 27 Jan 2025 16:19:12 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1738023552; x=1738628352; 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=FyWNhxH6MX4PSnLrusFQ6xLkdCKVe99tiY8VrHD30o8=; b=vsFNPTJYytpV5mlajIby3FnKmuDhYJ0+caHHX55JEOn1bk5R/Phvn58GjschL4tXxv n4xKp/yRraox7tbIxhV10C8+SGO8HB21BZgyJKCURurt22i+bQ8uj/BEt2ObQn/W/ViR rE5pSQJZxVw0HHjOpNF1u6MchPSGobIgDJvuUXxOcvch2wc8Rbd0IEBBoJN9/LijMfjH 9r8qq9lW4eob2vLLd32b1NCBr+Kq5cb4M0Q+fY+Myc27amDVDWd1ayFWcfCAomNwbkjb 7+H6rtcpwo3c/P3l6llPas+grU1kI/E0Rb4mAV2ihQU+wcTpnXFmRNrTmsvT+hvmq6kL GqsQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1738023552; x=1738628352; 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=FyWNhxH6MX4PSnLrusFQ6xLkdCKVe99tiY8VrHD30o8=; b=d2Kq7X9PZWPixVtDDVoPLIPnUh1hUhdlyYX0pyjntJpJazFIQSQljmSpN57m0lqn5n lXWq1skHPA6Ihp1WAzKoOQyAq3Y+6iAUlmFXPk24n7RHhyWem3b4zbo1RmMwUXMfq2gA vAs0c1CZL2MYTllziHE67ooscuuCHYzfPUiIQJKAm462TNuhfd7lHuLW+lxwXxS9dQMc UUooXCX0QxCuvBizXliyD7AND9kg/iy8KYlJATqUr7L0Xw3Z6hq0SUpnfP1nP2hehyCH cv3OhbzRCQD4XP2OfMzrER1ALlKWZHm3lv63kkOe9WCUtBseEz98jsQRnmrlq+HHr1Tl o5Gw== X-Gm-Message-State: AOJu0YxsDWAm42m2Up81v5zXWnsDjbI6HwLXxiHGq4oXxbC3Gn+AdykT FyMpZJKfgwgpfu/cNcuHxczK/5namCqziZ4G987fU4QQIQuEGfhNmPSegrJmDLUvecy7R6rTzM0 dZHN7aNANko+EEHG3yCZyHGBHxaDoM4hx55wkpOYZvRFCSz+Xc2GYs42W8uHgOXrZ9fB6jP9Q13 g8giKadQnRltDxo1WCyYEM04cbb2BDtP4+vEkYink= X-Google-Smtp-Source: AGHT+IFQ0k3vbcGgjJiFO+J21/D2vyau3B+J8kPOZBCb/gJiTCjGbZeXHjHZMdzoBTq6EzhdYkv1HmyqXn9kjQ== X-Received: from pfav8.prod.google.com ([2002:a05:6a00:ab08:b0:728:b8e3:993f]) (user=steadmon job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6a21:3385:b0:1e1:f281:8cf5 with SMTP id adf61e73a8af0-1eb2114c60amr70590470637.0.1738023551753; Mon, 27 Jan 2025 16:19:11 -0800 (PST) Date: Mon, 27 Jan 2025 16:19:03 -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 v7 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 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 | 38 +++++++++++++ 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 | 22 ++++++++ contrib/libgit-sys/public_symbol_export.h | 8 +++ contrib/libgit-sys/src/lib.rs | 46 +++++++++++++++ t/Makefile | 10 ++++ 10 files changed, 252 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..92989d3311 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,22 @@ $(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 + +contrib/libgit-sys/partial_symbol_export.o: contrib/libgit-sys/public_symbol_export.o libgit.a reftable/libreftable.a xdiff/lib.a + $(LD) -r $^ -o $@ + +contrib/libgit-sys/hidden_symbol_export.o: contrib/libgit-sys/partial_symbol_export.o + $(OBJCOPY) --localize-hidden $^ $@ + +contrib/libgit-sys/libgitpub.a: contrib/libgit-sys/hidden_symbol_export.o + $(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..cd1602206e --- /dev/null +++ b/contrib/libgit-sys/public_symbol_export.c @@ -0,0 +1,22 @@ +/* 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 00:19:04 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Josh Steadmon X-Patchwork-Id: 13951952 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 63A96BA33 for ; Tue, 28 Jan 2025 00:19:14 +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=1738023555; cv=none; b=UpH5ueRE+6Ihn+sicTTRVv5G5ddNLhQyCs4Z3xltf+0APrJ3Z7wTPosrjZ3SHhOXR/qWM4Jwtaot7vGTpf88I19hNQIDJgSR1dp2uXDRAUOn7ZKw+sZbge4dFqwhs1dXqJ019Kg5lDbBCaC7/oFUef9a4lg/8Lnz3St9t8q4Z/A= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1738023555; c=relaxed/simple; bh=sL6cLlp0qCOm3gz/6UgONc3eP3aFZ0Ax78YoiBPnd+c=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=Y8N06RhAjNwKLtyigN6TYQeOvN3JmrhQV/h/xS3u76awpSL09z/KHmoetIqm7p45s/cbMT3WIsbFJmk4SnFFtJc4B2C+0pm1alk/n3mCTzl2GEo08n7zDHpLpnTERlUu67e2NjOv4cbQhMyPNfufNOzZzbE8BQlPWzSFGD0OMn8= 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=Ea8OqiZr; 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="Ea8OqiZr" Received: by mail-pj1-f73.google.com with SMTP id 98e67ed59e1d1-2eebfd6d065so14165436a91.3 for ; Mon, 27 Jan 2025 16:19:14 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1738023554; x=1738628354; 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=2MLS6F2JhyFTcyx9hLtjX6V7cimjJMFhQptIdN5aZzs=; b=Ea8OqiZr8Kbbf441sdH/Bj5KcLLCYFD+rVWnAomZoHUQ/1+kwEThjQJV5HJ8iljjRk maFQR2kBEaOeAXn0GUmfSu2ATsUptD0Fkbs8G4G9XpCqh73q3m6BcnjpkY9+F+bpvGq/ g7/mnd0Oi8Au2hZIKX6TWK1wFktanmrQmGFJ1dTcwG7VaMpvY2V8DMgTOyW6fHj566Fx Yyao7GFLz5x3t+tdteTAsxuF+aLAnG7t7xr2NyhIbXvwjqNFn6cBlBNWgJid/OEMhOMC 6Na6PRr/2UqXuryDqKi/PGomlaD4gmNkGSETAFD0wXUht8u3U1e9/ZOQ2Zjh72n4s51L elwQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1738023554; x=1738628354; 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=2MLS6F2JhyFTcyx9hLtjX6V7cimjJMFhQptIdN5aZzs=; b=ZQPz1fGgzLzpxtMkGNgTruCZ82m2v37Mhqb1qFsQFSMFT2WZiM2w5Xgzg+ubFKOG1z BnIvL6HUVNzsWL6dCDw/375T80Tnwh3jgwqKT4klCG7cM5joOGkHx+hdpoVD6Pe7axGJ pPdQvtf6OhuveB6l/1m0X4j1/Sp7k4KZipv5muEEaycleNTh8uFzplIvvGweikxvbNi2 zlhWbiTPZN/p4V3h8sTGVMKEwKVtH9Ec2ARCCT1nHDjZf8x2oTuLVw6upYgY/3OW2N/2 RzOCPMYo816D6H6QUqVAS8DqM9JgYsjNwWM3sk2Qn6+Jd3KBWCRm0AlDg60SEjo2toX0 4t0g== X-Gm-Message-State: AOJu0YyGriHjKSXXub5c28qvsVihkapgdwjQJobbj54SowqqSfsHwDdB IJMmb5DMaKN3BYRWyagz8LPrpH4ETHVmcrudaeGJZmQwR+JDiVVWY1FG2A97WZSoyzWDawnPrPc GwGOZ03LQFTBkDn8iYQQrBglhbLQFK4yDYowj4hbfTko32vdqz9x1SmlJwSYUkx1h1S8TzUhueg Rd40TWFUF496jOLBN5ZLq2kkz1C+FOK/vwXwLbyaY= X-Google-Smtp-Source: AGHT+IH1DNYdCklmT5/KihdKFw/9sveIIiUQ5xS1X8qPC3QyoB0s8m7JI837QdlwKor54CSawJJlEhALtIoJMg== X-Received: from pjwx5.prod.google.com ([2002:a17:90a:c2c5:b0:2f5:4762:e778]) (user=steadmon job=prod-delivery.src-stubby-dispatcher) by 2002:a17:90a:c888:b0:2f6:f32e:90ac with SMTP id 98e67ed59e1d1-2f782c90095mr65016909a91.11.1738023553678; Mon, 27 Jan 2025 16:19:13 -0800 (PST) Date: Mon, 27 Jan 2025 16:19:04 -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 v7 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 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 | 29 +++++++++++++++++++++ contrib/libgit-sys/public_symbol_export.h | 10 ++++++++ contrib/libgit-sys/src/lib.rs | 31 ++++++++++++++++++++++- 3 files changed, 69 insertions(+), 1 deletion(-) diff --git a/contrib/libgit-sys/public_symbol_export.c b/contrib/libgit-sys/public_symbol_export.c index cd1602206e..a0297cb1a5 100644 --- a/contrib/libgit-sys/public_symbol_export.c +++ b/contrib/libgit-sys/public_symbol_export.c @@ -4,11 +4,40 @@ */ #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 *libgit_configset_alloc(void) +{ + struct config_set *cs = xmalloc(sizeof(struct config_set)); + git_configset_init(cs); + return (struct libgit_config_set *) cs; +} + +void libgit_configset_free(struct libgit_config_set *cs) +{ + git_configset_clear((struct config_set *) cs); + free((struct config_set *) cs); +} + +int libgit_configset_add_file(struct libgit_config_set *cs, const char *filename) +{ + return git_configset_add_file((struct config_set *) cs, filename); +} + +int libgit_configset_get_int(struct libgit_config_set *cs, const char *key, int *dest) +{ + return git_configset_get_int((struct config_set *) cs, key, dest); +} + +int libgit_configset_get_string(struct libgit_config_set *cs, const char *key, char **dest) +{ + return git_configset_get_string((struct config_set *) 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 00:19:05 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Josh Steadmon X-Patchwork-Id: 13951953 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 5FBEEF9F8 for ; Tue, 28 Jan 2025 00:19:16 +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=1738023559; cv=none; b=ctaWUC7TeEKRwM4JMVgR26IdDsq+L26WUxmIdCu5c1ApHq6/Cb7pUPnvovzg3VXFcAmj/SFSXSMgnlTNunbfaUpajCQ7bARuXpARi17A4eTCt9zo1SBllFipYQAXCjDfrl6OsDPJTLxuY05CxYSZb8FmRT1p5Z8S5M1DWkz/Aj8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1738023559; c=relaxed/simple; bh=C4prngDzo+YZtEUjDYEQ0QXTYEN+dptBKqb6lsscX9I=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=VxCqP/kH+V3CbFEcCsvOPCw2tVsDNmT4zcL2UVbIpQffo04SNSNA4KJAiDfaP5wIZOid8XVR2kDCrpMH/i1Ps6I+ZOHML23ExjSxA6uafcOzBlkgIQIl+NTa6RKg7WJS3Gg/09t64vuj6kRE+2ySLU/r0fIX3r63MkNFiyxk1/g= 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=GHJa4Nm7; 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="GHJa4Nm7" Received: by mail-pj1-f74.google.com with SMTP id 98e67ed59e1d1-2f81a0d0a18so3539452a91.3 for ; Mon, 27 Jan 2025 16:19:16 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1738023555; x=1738628355; 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=qMpvGQqX3m0EsIbNw7ZzudTz8bDSttxLLYcodOeK5hg=; b=GHJa4Nm7Zvys/jeY5IK84NjK1VNVrt2/wMhVS6MbvF/nAeKWF6YgEycsMeK21za3ST un1JnS5ZwfSZClwhda63/LMd7LhOYcnbG9Re8zwY+huzSN5KmtB7aaSecUHHZASQieD3 AWSoVkKI45DvYEI9JkE3AB1+45YUHNM4u40Bp97hTWMob7SzvwzxTcoeaBRuMDyQZ0G9 e5HtQ2BSmsPtieqCqR7azxZQQ3gyVcHezJsBy5QNrTQ2PKGaGNONSOoGP5pwzh11VjBq xnoZUOU3BqpYyioLVO4aoEhERaHG97cY9pRv1Xb8EdBkoS+QZg0tsLky8AtUl31+4odL AAeA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1738023555; x=1738628355; 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=qMpvGQqX3m0EsIbNw7ZzudTz8bDSttxLLYcodOeK5hg=; b=p9eHJfEh+Zg9LUk+7pSwR6+6d6Fzj5A/asBlhkHVB2CAkOMBDvryntdJlOS7qShv4y Cl5/Omjtxb0uVKzI8zTEhKqrxGYyn8EcE++Hbr/vMlzEcVHNgtg7DVHMeUR3/TB7kIdr htfdPtIn0mbEurCug01BFqQRAGocyf8LwEbKLfhw+pTrQYawy4IXW/WyMUGYwHf9uCFz i/qQQQb+ZtlUixNWlkhiQ+h6s+MQZSSeWIQ3hdWaQ3/RnZWThxdhb/3MC/DZurQkPzTK xQrVPU49nx6Ff2mDdLP57ClDaW1+VzL51MvfIEODFo2tXvuMwqrRnJblDLK7jhcl71a1 RqEg== X-Gm-Message-State: AOJu0Yxw41GpfuxCZ3i16iPRQEuRYWjtcvWCa1LW0KcYI61Hm6uBBtes oLrx7aUYzBbBnxfI7NaKz8gOe8ZA6dVgCgDazH58AcqrEwSl4JrMETiabAr08HyzQwauQTAOFly HfERgiRvJ3xWKJoRDB6sBgwcev3eQpvx4w47QooACR65558d1F9t9JU3cCpjlXSLNUh90CSExgv 3hbfOxog4x07nqCCLeyllmlGPsxDHXUVtPoqUvsAI= X-Google-Smtp-Source: AGHT+IGZhmYujqWWvjal5wvxjS2HhzUUYXHLzkCbWup63XizILfsWEX0kNjItAX+OdFsGnXmJUaC8Uc94LucFQ== X-Received: from pja7.prod.google.com ([2002:a17:90b:5487:b0:2f5:63a:4513]) (user=steadmon job=prod-delivery.src-stubby-dispatcher) by 2002:a17:90b:1f81:b0:2ee:f22a:61dd with SMTP id 98e67ed59e1d1-2f782d9a170mr56396838a91.32.1738023555544; Mon, 27 Jan 2025 16:19:15 -0800 (PST) Date: Mon, 27 Jan 2025 16:19:05 -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: <88425bb0b1b90cf6b3d2d5a1af57e46c7a03ad39.1738023208.git.steadmon@google.com> Subject: [PATCH v7 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 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 | 13 ++-- 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, 245 insertions(+), 6 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 92989d3311..37b63de079 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,19 @@ 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 +.PHONY: libgit-sys libgit-rs libgit-sys: $(QUIET)(\ cd contrib/libgit-sys && \ cargo build \ ) +libgit-rs: + $(QUIET)(\ + cd contrib/libgit-rs && \ + cargo build \ + ) ifdef INCLUDE_LIBGIT_RS -all:: libgit-sys +all:: libgit-sys libgit-rs endif contrib/libgit-sys/partial_symbol_export.o: contrib/libgit-sys/public_symbol_export.o libgit.a reftable/libreftable.a xdiff/lib.a 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