Skip to contentSkip to conversation

Introduce cgit-rs, a Rust wrapper around libgit.a

RFC PATCH Superseded Josh Steadmon 13 participants 217 messages Aug 7, 2024 – Jan 30, 2025 Page 2 of 2

Subsystems (2)
lore
Share & tools
lore
#178 lore raw

Re: [PATCH v6 2/5] libgit-sys: introduce Rust wrapper for libgit.a

In reply to Junio C Hamano
Quoted 18 lines · Junio C Hamano

Makefile CURL_CONFIG = curl-config · line 661

Quoted 10 lines · Josh Steadmon
Makefile +2 -0
index 27e68ac039..47e864a861 100644
@@ -657,6 +657,8 @@ CURL_CONFIG = curl-config
 GCOV = gcov
 STRIP = strip
 SPATCH = spatch
+LD = ld
+OBJCOPY = objcopy

This assumes GNU binutils is available. As long as our intention is to start the Rust support as an optional feature, that is OK. Hopefully the piece that requires $(OBJCOPY) is arranged to be easily opted out. Let's keep reading.

Quoted 10 lines

Makefile OBJECTS += $(REFTABLE_OBJS) $(REFTABLE_TEST_OBJS) · line 2736

Quoted 5 lines
Makefile +1 -0
@@ -2731,6 +2733,7 @@ OBJECTS += $(REFTABLE_OBJS) $(REFTABLE_TEST_OBJS)
 OBJECTS += $(UNIT_TEST_OBJS)
 OBJECTS += $(CLAR_TEST_OBJS)
 OBJECTS += $(patsubst %,$(UNIT_TEST_DIR)/%.o,$(UNIT_TEST_PROGRAMS))
+OBJECTS += contrib/libgit-sys/public_symbol_export.o

This is compiled for everybody, even for those whose platform cannot support Rust interface (or those who choose not to build it). As long as what is in the file is written portably, it is fine to have stubs and entry points that their build will not use.

Later on in the series we add an INCLUDE_LIBGIT_RS variable to control builds and tests; I don't see any reason why we can't move that earlier in the series, so I'll do so in V7 and then look at what we need to do to make things more portable.

Quoted 109 lines

Makefile clean: profile-clean coverage-clean cocciclean · line 3735

Quoted 10 lines
Makefile +4 -0
 ifndef NO_CURL
 	OBJECTS += http.o http-walker.o remote-curl.o
@@ -3726,6 +3729,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) -r contrib/libgit-sys/partial_symbol_export.o
+	$(RM) -r contrib/libgit-sys/hidden_symbol_export.o
+	$(RM) -r contrib/libgit-sys/libgitpub.a

Which one of the above is a directory? The latter three smells like a regular file, so we shouldn't say "-r" there.

Makefile $(CLAR_TEST_PROG): $(UNIT_TEST_DIR)/clar.suite $(CLAR_TEST_OBJS) $(GITLIBS) GIT- · line 3899

Quoted 7 lines
Makefile +3 -0
@@ -3887,3 +3894,12 @@ $(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
+
+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 $@

OK. We build a "relocatable" object, which is unconditionally made as part of $(OBJECTS) above. Even without GNU binutils "ld", people hopefully can convince their linker to do the equivalent. I am not sure if it is healthy to assume that such a linker also uses "-r" for the feature, so we may have to make this rule more customizable, or make partial_symbol_export.o only conditionally part of $(OBJECTS) to allow them to opt out.

Makefile $(CLAR_TEST_PROG): $(UNIT_TEST_DIR)/clar.suite $(CLAR_TEST_OBJS) $(GITLIBS) GIT- · line 3902

Quoted 2 lines
Makefile +2 -0
+contrib/libgit-sys/hidden_symbol_export.o: contrib/libgit-sys/partial_symbol_export.o
+	$(OBJCOPY) --localize-hidden $^ $@

Unlike the "public" thing, hidden_symbol_export.o was not made part of $(OBJECTS), so this part is arranged to allow people without $(OBJCOPY) to easily opt out of this part of the system, which is good.

Makefile $(CLAR_TEST_PROG): $(UNIT_TEST_DIR)/clar.suite $(CLAR_TEST_OBJS) $(GITLIBS) GIT- · line 3905

Quoted 2 lines
Makefile +2 -0
+contrib/libgit-sys/libgitpub.a: contrib/libgit-sys/hidden_symbol_export.o
+	$(AR) $(ARFLAGS) $@ $^

Likewise, people can easily opt out of building "libgitpub.a", which is good (these targets are triggered only from build.rs).

contrib/libgit-sys/README.md line 4

Quoted 10 lines
contrib/libgit-sys/README.md new file +4 -0
new file mode 100644
index 0000000000..c061cfcaf5
@@ -0,0 +1,4 @@
+# libgit-sys
+
+A small proof-of-concept crate showing how to provide a Rust FFI to Git
+internals.

OK.

contrib/libgit-sys/public_symbol_export.c line 3

Quoted 9 lines
contrib/libgit-sys/public_symbol_export.c new file +3 -0
new file mode 100644
index 0000000000..7cd5007902
@@ -0,0 +1,21 @@
+// 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.

Style.

Quoted 7 lines
contrib/libgit-sys/public_symbol_export.c new file +7 -0
+#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)

contrib/libgit-sys/public_symbol_export.c line 21

Quoted 10 lines
contrib/libgit-sys/public_symbol_export.c new file +10 -0
+{
+	return git_user_agent();
+}
+
+const char *libgit_user_agent_sanitized(void)
+{
+	return git_user_agent_sanitized();
+}
+
+#pragma GCC visibility pop

I do not think we would mind not having Rust binding support on platforms without GCC (and clang---I assume it would be aware of and react to that #pragma GCC the same way?). But do we allow this file to be left uncompiled when the build wants to opt out of Rust support?

Quoted 6 lines
contrib/libgit-sys/public_symbol_export.h new file +0 -0
new file mode 100644
index 0000000000..a3372f93fa

contrib/libgit-sys/public_symbol_export.h line 8

Quoted 8 lines
contrib/libgit-sys/public_symbol_export.h new file +8 -0
@@ -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 */

OK.

#179 lore raw

Re: [PATCH v6 2/5] libgit-sys: introduce Rust wrapper for libgit.a

In reply to Junio C Hamano
Quoted 27 lines · Junio C Hamano

contrib/libgit-sys/public_symbol_export.c line 3

Quoted 9 lines · Josh Steadmon
contrib/libgit-sys/public_symbol_export.c new file +3 -0
new file mode 100644
index 0000000000..7cd5007902
@@ -0,0 +1,21 @@
+// 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.

Style.

Quoted 13 lines
contrib/libgit-sys/public_symbol_export.c new file +13 -0
+#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)
+{
Quoted 8 lines

contrib/libgit-sys/public_symbol_export.c line 21

Quoted 4 lines
contrib/libgit-sys/public_symbol_export.c new file +4 -0
+	return git_user_agent_sanitized();
+}
+
+#pragma GCC visibility pop

I do not think we would mind not having Rust binding support on platforms without GCC (and clang---I assume it would be aware of and react to that #pragma GCC the same way?).

Yes, GCC and clang both handle this the same way.

Quoted 3 lines

But do we allow this file
to be left uncompiled when the build wants to opt out of Rust support?

Yes, in V7 this will only be built if INCLUDE_LIBGIT_RS is set.

#180 lore raw

PATCH v6 3/5 libgit-sys: also export some config_set functions

In reply to Josh Steadmon

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.

---
 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(-)
contrib/libgit-sys/public_symbol_export.c +29 -0
index 7cd5007902..1799fc6fde 100644
@@ -3,11 +3,40 @@
 // avoids conflicts with other libraries such as libgit2.
 
 #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();
contrib/libgit-sys/public_symbol_export.h +10 -0
index a3372f93fa..701db92d53 100644
@@ -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);
contrib/libgit-sys/src/lib.rs +30 -1
index d4853f3074..dadb4e5f40 100644
@@ -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)]
Signature · 1 line
2.48.0.rc2.279.g1de40edade-goog
#181 lore raw

PATCH v6 4/5 libgit: add higher-level libgit crate

In reply to Josh Steadmon

From: Calvin Wan <[email protected]>

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

---
 .gitignore                         |  1 +
 Makefile                           |  2 +-
 contrib/libgit-rs/Cargo.lock       | 77 ++++++++++++++++++++++++
 contrib/libgit-rs/Cargo.toml       | 15 +++++
 contrib/libgit-rs/README.md        | 13 ++++
 contrib/libgit-rs/build.rs         |  4 ++
 contrib/libgit-rs/src/lib.rs       | 95 ++++++++++++++++++++++++++++++
 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 ++
 11 files changed, 216 insertions(+), 1 deletion(-)
 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/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
.gitignore +1 -0
index 31d7e64287..acdd8ce7c7 100644
@@ -250,4 +250,5 @@ Release/
 /git.VC.db
 *.dSYM
 /contrib/buildsystems/out
+/contrib/libgit-rs/target
 /contrib/libgit-sys/target
Makefile +1 -1
index 47e864a861..230d366457 100644
@@ -3729,7 +3729,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-rs/target contrib/libgit-sys/target
 	$(RM) -r contrib/libgit-sys/partial_symbol_export.o
 	$(RM) -r contrib/libgit-sys/hidden_symbol_export.o
 	$(RM) -r contrib/libgit-sys/libgitpub.a
contrib/libgit-rs/Cargo.lock new file +77 -0

77 lines not on this page · raw message

contrib/libgit-rs/Cargo.toml new file +15 -0
new file mode 100644
index 0000000000..634435cd6c
@@ -0,0 +1,15 @@
+[package]
+name = "libgit"
+version = "0.1.0"
+edition = "2021"
+build = "build.rs"
+rust-version = "1.63"
+
+[lib]
+path = "src/lib.rs"
+
+[dependencies]
+libgit-sys = { version = "0.1.0", path = "../libgit-sys" }
+
+[build-dependencies]
+autocfg = "1.4.0"
contrib/libgit-rs/README.md new file +13 -0
new file mode 100644
index 0000000000..ff945e1ce2
@@ -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).
contrib/libgit-rs/build.rs new file +4 -0
new file mode 100644
index 0000000000..f8bd01a690
@@ -0,0 +1,4 @@
+pub fn main() {
+    let ac = autocfg::new();
+    ac.emit_has_path("std::ffi::c_char");
+}
contrib/libgit-rs/src/lib.rs new file +95 -0

95 lines not on this page · raw message

contrib/libgit-rs/testdata/config1 new file +2 -0
new file mode 100644
index 0000000000..4e9a9d25d1
@@ -0,0 +1,2 @@
+[trace2]
+	eventNesting = 1
contrib/libgit-rs/testdata/config2 new file +2 -0
new file mode 100644
index 0000000000..b8d1eca423
@@ -0,0 +1,2 @@
+[trace2]
+	eventTarget = 1
contrib/libgit-rs/testdata/config3 new file +2 -0
new file mode 100644
index 0000000000..ca7b9a7c38
@@ -0,0 +1,2 @@
+[trace2]
+	eventNesting = 3
contrib/libgit-sys/src/lib.rs +4 -0
index dadb4e5f40..4bfc650450 100644
@@ -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;
 
Signature · 1 line
2.48.0.rc2.279.g1de40edade-goog
#182 lore raw

Re: [PATCH v6 4/5] libgit: add higher-level libgit crate

Quoted 74 lines · Josh Steadmon

From: Calvin Wan <[email protected]>

contrib/libgit-rs/Cargo.lock new file +67 -0
new file mode 100644
index 0000000000..a30c7c8d33
@@ -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]]

contrib/libgit-rs/Cargo.lock line 77

Quoted 10 lines
contrib/libgit-rs/Cargo.lock new file +10 -0
+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"

There are two possibilities here. The first is to check in the Cargo.lock, in which case all users will have to use these versions. That produces a more stable and reliable approach, but it has some downsides.

Say, for instance, that a cool new platform or architecture is added to libc and we'd like to support it, but that version of libc requires a newer version of Rust. We then would have to hold off supporting that new platform due to compatibility reasons. But if we omitted the Cargo.lock, users could install any version that meets their needs.

I believe Rust just got the ability to install only versions that honour the rust-version directive in 1.84, whereas older versions will try to use the latest version, even if that fails. So I think it's okay for now to use Cargo.lock, because that means that things will be better out of the box for users on older Rust. But we may want to drop it once 1.84 is older than our supported version.

Quoted 9 lines
contrib/libgit-rs/src/lib.rs new file +3 -0
new file mode 100644
index 0000000000..27b6fd63f1
@@ -0,0 +1,95 @@
+use std::ffi::{c_void, CStr, CString};
+use std::path::Path;
+

contrib/libgit-rs/src/lib.rs line 13

Quoted 10 lines
contrib/libgit-rs/src/lib.rs new file +10 -0
+#[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)]
+pub type c_char = i8;
+
+#[cfg(not(has_std__ffi__c_char))]
+#[allow(non_camel_case_types)]
+pub type c_int = i32;

By making these pub, you're exporting them. We probably do not want to do that, since they are not part of our API.

If we need them more generally in the code, let's put them in a module called ffi or such that's pub(crate), and then use them from there.

contrib/libgit-rs/src/lib.rs line 18

Quoted 4 lines
contrib/libgit-rs/src/lib.rs new file +4 -0
+use libgit_sys::*;
+
+pub struct ConfigSet(*mut libgit_config_set);
+impl ConfigSet {

I would suggest we place these in a module, such as config. We should expect to have a lot more things in our crate in the future and putting a little thought into this now will make it easier for users in the future.

I'd also, of course, suggest documentation comments, since if we ever upload this to crates.io, people are overwhelmingly going to read only the docs and not the source, and right now we've said nothing about how this works or should work.

Quoted 10 lines
contrib/libgit-rs/src/lib.rs new file +10 -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"),

contrib/libgit-rs/src/lib.rs line 95

Quoted 10 lines
contrib/libgit-rs/src/lib.rs new file +10 -0
+            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);
+    }
+}

I am, of course, delighted to see tests. This is a nice improvement in our code that we can take advantage of, and we can test both the C code and Rust code at the same time. And if, in the future, we decide that we'd like to implement a Rust-based version of this API to replace the C one, we've already written tests.

Signature · 2 lines
brian m. carlson (they/them or he/him)
Toronto, Ontario, CA
#183 lore raw

Re: [PATCH v6 4/5] libgit: add higher-level libgit crate

Quoted 97 lines · brian m. carlson
Quoted 74 lines · Josh Steadmon

From: Calvin Wan <[email protected]>

contrib/libgit-rs/Cargo.lock new file +67 -0
new file mode 100644
index 0000000000..a30c7c8d33
@@ -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]]

contrib/libgit-rs/Cargo.lock line 77

Quoted 10 lines
contrib/libgit-rs/Cargo.lock new file +10 -0
+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"

There are two possibilities here. The first is to check in the Cargo.lock, in which case all users will have to use these versions. That produces a more stable and reliable approach, but it has some downsides.

Say, for instance, that a cool new platform or architecture is added to libc and we'd like to support it, but that version of libc requires a newer version of Rust. We then would have to hold off supporting that new platform due to compatibility reasons. But if we omitted the Cargo.lock, users could install any version that meets their needs.

Quoted 6 lines

I believe Rust just got the ability to install only versions that honour the rust-version directive in 1.84, whereas older versions will try to use the latest version, even if that fails. So I think it's okay for now to use Cargo.lock, because that means that things will be better out of the box for users on older Rust. But we may want to drop it once 1.84 is older than our supported version.

OK, I'll add a TODO comment in Cargo.toml.

Quoted 14 lines
Quoted 14 lines
contrib/libgit-rs/src/lib.rs new file +8 -0
new file mode 100644
index 0000000000..27b6fd63f1
@@ -0,0 +1,95 @@
+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)]
Quoted 8 lines

contrib/libgit-rs/src/lib.rs line 13

Quoted 5 lines
contrib/libgit-rs/src/lib.rs new file +5 -0
+pub type c_char = i8;
+
+#[cfg(not(has_std__ffi__c_char))]
+#[allow(non_camel_case_types)]
+pub type c_int = i32;

By making these pub, you're exporting them. We probably do not want to do that, since they are not part of our API.

Fixed for V7.

Quoted 2 lines

If we need them more generally in the code, let's put them in a module called ffi or such that's pub(crate), and then use them from there.

Hmm, I guess we'd need to do that for libgit-sys as well? Or maybe not, since they're part of the API and thus we should just keep the current pub type ... setup?

Quoted 9 lines

contrib/libgit-rs/src/lib.rs line 18

Quoted 4 lines
contrib/libgit-rs/src/lib.rs new file +4 -0
+use libgit_sys::*;
+
+pub struct ConfigSet(*mut libgit_config_set);
+impl ConfigSet {

I would suggest we place these in a module, such as config. We should expect to have a lot more things in our crate in the future and putting a little thought into this now will make it easier for users in the future.

ACK, will do for V7.

Quoted 4 lines

I'd also, of course, suggest documentation comments, since if we ever upload this to crates.io, people are overwhelmingly going to read only the docs and not the source, and right now we've said nothing about how this works or should work.

ACK.

Quoted 18 lines
Quoted 18 lines
contrib/libgit-rs/src/lib.rs new file +18 -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);
Quoted 8 lines

contrib/libgit-rs/src/lib.rs line 37

Quoted 2 lines
contrib/libgit-rs/src/lib.rs new file +2 -0
+    }
+}

I am, of course, delighted to see tests. This is a nice improvement in our code that we can take advantage of, and we can test both the C code and Rust code at the same time. And if, in the future, we decide that we'd like to implement a Rust-based version of this API to replace the C one, we've already written tests.

Thanks for the review and advice (both for this round and all the previous ones), it's much appreciated.

Quoted 3 lines
Signature · 2 lines
brian m. carlson (they/them or he/him)
Toronto, Ontario, CA
#184 lore raw

PATCH v6 5/5 Makefile: add option to build and test libgit-rs and libgit-rs-sys

In reply to Josh Steadmon

From: Calvin Wan <[email protected]>

Add libgitrs, libgitrs-sys, libgitrs-test, and libgitrs-sys-test targets to their respective Makefiles so they can be built and tested without having to run cargo build/test.

Add build variable, INCLUDE_LIBGIT_RS, that when set, automatically builds and tests libgit-rs and libgit-rs-sys when make all is run.

---
 Makefile                    | 26 ++++++++++++++++++++++++++
 contrib/libgit-sys/build.rs |  2 +-
 t/Makefile                  | 16 ++++++++++++++++
 3 files changed, 43 insertions(+), 1 deletion(-)
Makefile +26 -0
index 230d366457..133090a145 100644
@@ -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 crates in contrib/libgit-rs/ and contrib/libgit-rs/libgit-sys/.
+#
 # === Optional library: libintl ===
 #
 # Define NO_GETTEXT if you don't want Git output to be translated.
@@ -2238,6 +2241,13 @@ 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-rs/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
@@ -3895,6 +3905,22 @@ 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 \
+	)
+.PHONY: libgit-rs
+libgit-rs:
+	$(QUIET)(\
+		cd contrib/libgit-rs && \
+		cargo build \
+	)
+ifdef INCLUDE_LIBGIT_RS
+all:: 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
 	$(LD) -r $^ -o $@
 
contrib/libgit-sys/build.rs +1 -1
index b6c65193bc..3ffd80ad91 100644
@@ -14,7 +14,7 @@ pub fn main() -> std::io::Result<()> {
         .env_remove("PROFILE")
         .current_dir(git_root.clone())
         .args([
-            "CFLAGS=-fvisibility=hidden",
+            "INCLUDE_LIBGIT_RS=YesPlease",
             "contrib/libgit-sys/libgitpub.a",
         ])
         .output()
t/Makefile +16 -0
index daa5fcae86..45fe6089b2 100644
@@ -177,3 +177,19 @@ 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 \
+	)
+.PHONY: libgit-rs-test
+libgit-rs-test:
+	$(QUIET)(\
+		cd ../contrib/libgit-rs && \
+		cargo test \
+	)
+ifdef INCLUDE_LIBGIT_RS
+all:: libgit-sys-test libgit-rs-test
+endif
Signature · 1 line
2.48.0.rc2.279.g1de40edade-goog
#185 lore raw

Re: [PATCH v6 0/5] Introduce libgit-rs, a Rust wrapper around libgit.a

In reply to Josh Steadmon
Quoted 4 lines · Josh Steadmon

Apologies for the long delay on V6; I am finally back after several months of $DAYJOB firefighting, holidays, and sick leave. I should have time to devote to this series again, but given the lack of feedback on V5 I am hopeful that this will be the final iteration of this series.

Thanks and welcome back ;-)

Given the lack of feedback on the previous round, I hope we will see enthused support on the topic. Otherwise it is hard to tell if the previous lack of feedback was merely lack of interest, or lack of anything lacking in the series.

Quoted 8 lines

There is known NEEDSWORK, but I feel that they can be addressed in follow-up changes, rather than in this series. If you feel otherwise, please let me know:

* Investigate alternative methods of managing symbol visibility & renaming.

* Figure out symbol versioning

OK. Let's see what people find out.

#186 lore raw

Re: [PATCH v6 0/5] Introduce libgit-rs, a Rust wrapper around libgit.a

In reply to Josh Steadmon
Quoted 14 lines · Josh Steadmon

Apologies for the long delay on V6; I am finally back after several months of $DAYJOB firefighting, holidays, and sick leave. I should have time to devote to this series again, but given the lack of feedback on V5 I am hopeful that this will be the final iteration of this series.

This series provides two small Rust wrapper libraries around parts of
Git: "libgit-sys", which exposes a few functions from libgit.a, and "libgit", which provides a more Rust-friendly interface to some of those functions. In addition to included unit tests, at $DAYJOB we have tested building JJ[1] with our library and used it to replace some of the libgit2-rs uses.

[1] https://github.com/martinvonz/jj

Quoted 8 lines

There is known NEEDSWORK, but I feel that they can be addressed in follow-up changes, rather than in this series. If you feel otherwise, please let me know:

* Investigate alternative methods of managing symbol visibility & renaming.

* Figure out symbol versioning

It looks like we're building a general Rust lib crate and a static library here, so symbol versioning isn't an issue. I expect in the future we may want to provide a shared library, in which case we will indeed want to do that, but I agree that can wait until later.

In any event, I overall think this series is a nice improvement, and I am very enthusiastic about it. (This is mostly for the benefit of Junio, since I think the authors of this series already know that.) Once it lands, I do plan to build on it somewhat.

Signature · 2 lines
brian m. carlson (they/them or he/him)
Toronto, Ontario, CA
#187 lore raw

Re: [PATCH v6 0/5] Introduce libgit-rs, a Rust wrapper around libgit.a

Quoted 9 lines · brian m. carlson

It looks like we're building a general Rust lib crate and a static library here, so symbol versioning isn't an issue. I expect in the future we may want to provide a shared library, in which case we will indeed want to do that, but I agree that can wait until later.

In any event, I overall think this series is a nice improvement, and I am very enthusiastic about it. (This is mostly for the benefit of Junio, since I think the authors of this series already know that.) Once it lands, I do plan to build on it somewhat.

;-) Thanks, I heard you.

#188 lore raw

v7 cover Introduce libgit-rs, a Rust wrapper around libgit.a

In reply to Josh Steadmon

This series provides two small Rust wrapper libraries around parts of
Git: "libgit-sys", which exposes a few functions from libgit.a, and "libgit", which provides a more Rust-friendly interface to some of those functions. In addition to included unit tests, at $DAYJOB we have tested building JJ[1] with our library and used it to replace some of the libgit2-rs uses.

[1] https://github.com/jj-vcs/jj

There is known NEEDSWORK, but I feel that they can be addressed in follow-up changes, rather than in this series. If you feel otherwise, please let me know:

* Investigate alternative methods of managing symbol visibility & renaming.

* Figure out symbol versioning

Changes in V7:
* Moved the ConfigSet implementation in libgit-rs to a config module.

* Added doc comments for ConfigSet and its methods.

* Fix meson builds by adding new object files to libgit_sources

* Moved INCLUDE_LIBGIT_RS Makefile changes earlier in the series, so that we can make it optional to compile some of the libgitpub sources. Squashed V6 patch 5/5 into this series' patch 4/4.

* Don't publicly export FFI types in libgit-rs.

* Removed extraneous -r argument to $(RM) in the clean rules.

* Added TODO reminder in Cargo.toml about removing Cargo.lock once we hit a certain minimum supported Rust version.

* Style cleanup in public_symbol_export.c

Changes in V6:
* Rebased onto current master, since V5 was several months old.

* Move libgit-sys out of libgit-rs; while this sort of nesting is common in Rust crates with standalone repositories, it doesn't make as much sense when they're contained in the larger Git project's repo.

* Standardize the naming of some of the Makefile targets to always include a dash in the "-rs" or "-sys" suffixes.

* Clean up READMEs and crate descriptions in preparation for uploading to crates.io.

Changes in V5:
* When building with INCLUDE_LIBGIT_RS defined, add "-fvisibility=hidden" to CFLAGS. This allows us to manage symbol visibility in libgitpub.a without causing make all to rebuild from scratch due to changing CFLAGS.

* Avoid using c_int in the higher-level Rust API.

* Remove libgitpub.a and intermediate files with make clean.

Changes in V4:
* Drop V3 patch #3, which added wrappers around repository initialization and config access. These are not well-libified, and they are not necessary for JJ's proof-of-concept use case, so let's avoid exporting them for now.

* Set a minimum supported Rust version of 1.63. Autodetect whether our Rust version has c_int and c_char types; if not, define them ourselves.

* When building libgitpub.a via build.rs, set DEVELOPER=1 to catch additional errors at build time.

* In build.rs, use the make_cmd crate to portable select the correct invocation of GNU Make.

* Follow naming standards for _alloc() and _free() functions.

* Use String instead of CString in higher-level API.

* Move libgit_configset_alloc() and libgit_configset_free() out of upstream Git, to the libgitpub shim library.

* In libgitpub, initialize libgit_config_set structs in the _alloc() function rather than with a separate _init() function.

* Remove unnecessary comments in libgit-sys showing where the wrapped functions were originally defined.

* Fix clippy lint: don't reborrow configfile path references.

* Various typo fixes and cargo fmt fixes.

Changes in V3:
* Renamed cgit-rs to libgit-rs and cgit-sys to libgit-sys

* Makefile cleanup, particularly adding config.mak options that developers can set to run Rust builds and tests by default (Patch 6)

* Provide testdata configs for unit tests

* ConfigSet API now uses &Path instead of &str -- more ergonomic for Rust users to pass in and errors out if the path string isn't UTF-8

* Fixed unresolved dependency on libz in Cargo.toml

Calvin Wan (1):
libgit: add higher-level libgit crate

Josh Steadmon (3):
common-main: split init and exit code into new files
libgit-sys: introduce Rust wrapper for libgit.a
libgit-sys: also export some config_set functions

 .gitignore                                |   2 +
 Makefile                                  |  45 +++++++++
 common-exit.c                             |  26 ++++++
 common-init.c                             |  63 +++++++++++++
 common-init.h                             |   6 ++
 common-main.c                             |  83 +----------------
 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/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 |  51 +++++++++++
 contrib/libgit-sys/public_symbol_export.h |  18 ++++
 contrib/libgit-sys/src/lib.rs             |  79 ++++++++++++++++
 meson.build                               |   2 +
 t/Makefile                                |  15 +++
 24 files changed, 660 insertions(+), 81 deletions(-)
 create mode 100644 common-exit.c
 create mode 100644 common-init.c
 create mode 100644 common-init.h
 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
 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

Range-diff against v6:
1: ff6cd62397 ! 1: cd0cb9aa04 common-main: split init and exit code into new files
@@ common-main.c
-
- return code;
-}
+
+ ## meson.build ##
+@@ meson.build: 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',
2: 5fc66cdb16 ! 2: f1502b8590 libgit-sys: introduce Rust wrapper for libgit.a

    @@ .gitignore: Release/
     +/contrib/libgit-sys/target
     
      ## Makefile ##
    +@@ Makefile: 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.
     @@ Makefile: CURL_CONFIG = curl-config
      GCOV = gcov
      STRIP = strip
    @@ Makefile: CURL_CONFIG = curl-config
      
      export TCL_PATH TCLTK_PATH
      
    -@@ Makefile: OBJECTS += $(REFTABLE_OBJS) $(REFTABLE_TEST_OBJS)
    - OBJECTS += $(UNIT_TEST_OBJS)
    +@@ Makefile: 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
    +@@ Makefile: OBJECTS += $(UNIT_TEST_OBJS)
      OBJECTS += $(CLAR_TEST_OBJS)
      OBJECTS += $(patsubst %,$(UNIT_TEST_DIR)/%.o,$(UNIT_TEST_PROGRAMS))
    -+OBJECTS += contrib/libgit-sys/public_symbol_export.o
      
    ++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
     @@ Makefile: 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/partial_symbol_export.o
    -+	$(RM) -r contrib/libgit-sys/hidden_symbol_export.o
    -+	$(RM) -r contrib/libgit-sys/libgitpub.a
    ++	$(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
    @@ Makefile: $(CLAR_TEST_PROG): $(UNIT_TEST_DIR)/clar.suite $(CLAR_TEST_OBJS) $(GIT
      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/Cargo.toml (new)
     +edition = "2021"
     +build = "build.rs"
     +links = "gitpub"
    -+rust-version = "1.63"
    ++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/[email protected]/
     +description = "Native bindings to a portion of libgit"
     +
     +[lib]
    @@ contrib/libgit-sys/build.rs (new)
     +        .env_remove("PROFILE")
     +        .current_dir(git_root.clone())
     +        .args([
    -+            "CFLAGS=-fvisibility=hidden",
    ++            "INCLUDE_LIBGIT_RS=YesPlease",
     +            "contrib/libgit-sys/libgitpub.a",
     +        ])
     +        .output()
    @@ contrib/libgit-sys/build.rs (new)
     
      ## contrib/libgit-sys/public_symbol_export.c (new) ##
     @@
    -+// 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.
    ++/* 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"
    @@ contrib/libgit-sys/src/lib.rs (new)
     +        );
     +    }
     +}
    +
    + ## t/Makefile ##
    +@@ t/Makefile: 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
3:  03f39b6c3a < -:  ---------- libgit-sys: also export some config_set functions
5:  84706f0db7 ! 3:  d67d3648d1 Makefile: add option to build and test libgit-rs and libgit-rs-sys
    @@
      ## Metadata ##
    -Author: Calvin Wan <[email protected]>
    +Author: Josh Steadmon <[email protected]>
     
      ## Commit message ##
    -    Makefile: add option to build and test libgit-rs and libgit-rs-sys
    +    libgit-sys: also export some config_set functions
     
    -    Add libgitrs, libgitrs-sys, libgitrs-test, and libgitrs-sys-test targets
    -    to their respective Makefiles so they can be built and tested without
    -    having to run cargo build/test.
    +    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.
     
    -    Add build variable, INCLUDE_LIBGIT_RS, that when set, automatically
    -    builds and tests libgit-rs and libgit-rs-sys when `make all` is run.
    +    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: Josh Steadmon <[email protected]>
    +    Co-authored-by: Calvin Wan <[email protected]>
         Signed-off-by: Calvin Wan <[email protected]>
    +    Signed-off-by: Josh Steadmon <[email protected]>
     
    - ## Makefile ##
    -@@ Makefile: 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 crates in contrib/libgit-rs/ and contrib/libgit-rs/libgit-sys/.
    -+#
    - # === Optional library: libintl ===
    - #
    - # Define NO_GETTEXT if you don't want Git output to be translated.
    -@@ Makefile: ifdef FSMONITOR_OS_SETTINGS
    - 	COMPAT_OBJS += compat/fsmonitor/fsm-path-utils-$(FSMONITOR_OS_SETTINGS).o
    - endif
    + ## contrib/libgit-sys/public_symbol_export.c ##
    +@@
    +  */
      
    -+ifdef INCLUDE_LIBGIT_RS
    -+	# Enable symbol hiding in contrib/libgit-rs/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
    -@@ Makefile: 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
    + #include "git-compat-util.h"
    ++#include "config.h"
    + #include "contrib/libgit-sys/public_symbol_export.h"
    + #include "version.h"
      
    -+.PHONY: libgit-sys
    -+libgit-sys:
    -+	$(QUIET)(\
    -+		cd contrib/libgit-sys && \
    -+		cargo build \
    -+	)
    -+.PHONY: libgit-rs
    -+libgit-rs:
    -+	$(QUIET)(\
    -+		cd contrib/libgit-rs && \
    -+		cargo build \
    -+	)
    -+ifdef INCLUDE_LIBGIT_RS
    -+all:: 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
    - 	$(LD) -r $^ -o $@
    + #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();
     
    - ## contrib/libgit-sys/build.rs ##
    -@@ contrib/libgit-sys/build.rs: pub fn main() -> std::io::Result<()> {
    -         .env_remove("PROFILE")
    -         .current_dir(git_root.clone())
    -         .args([
    --            "CFLAGS=-fvisibility=hidden",
    -+            "INCLUDE_LIBGIT_RS=YesPlease",
    -             "contrib/libgit-sys/libgitpub.a",
    -         ])
    -         .output()
    + ## contrib/libgit-sys/public_symbol_export.h ##
    +@@
    + #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);
     
    - ## t/Makefile ##
    -@@ t/Makefile: perf:
    + ## contrib/libgit-sys/src/lib.rs ##
    +@@
    + #[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;
    ++
    + }
      
    - .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 \
    -+	)
    -+.PHONY: libgit-rs-test
    -+libgit-rs-test:
    -+	$(QUIET)(\
    -+		cd ../contrib/libgit-rs && \
    -+		cargo test \
    -+	)
    -+ifdef INCLUDE_LIBGIT_RS
    -+all:: libgit-sys-test libgit-rs-test
    -+endif
    + #[cfg(test)]

4: 65166ea0c0 ! 4: 88425bb0b1 libgit: add higher-level libgit crate
@@ .gitignore: Release/
/contrib/libgit-sys/target

## Makefile ##
+@@ Makefile: 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 ===
+ #
@@ Makefile: 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-rs/target contrib/libgit-sys/target
- $(RM) -r contrib/libgit-sys/partial_symbol_export.o
- $(RM) -r contrib/libgit-sys/hidden_symbol_export.o
- $(RM) -r contrib/libgit-sys/libgitpub.a
++ $(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
+@@ Makefile: 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

## contrib/libgit-rs/Cargo.lock (new) ##
@@
@@ contrib/libgit-rs/Cargo.toml (new)
+version = "0.1.0"
+edition = "2021"
+build = "build.rs"
-+rust-version = "1.63"
++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/[email protected]/
++
+
+[lib]
+path = "src/lib.rs"
@@ contrib/libgit-rs/build.rs (new)
+ ac.emit_has_path("std::ffi::c_char");
+}

- ## contrib/libgit-rs/src/lib.rs (new) ##
+ ## contrib/libgit-rs/src/config.rs (new) ##
@@
+use std::ffi::{c_void, CStr, CString};
+use std::path::Path;
@@ contrib/libgit-rs/src/lib.rs (new)
+
+#[cfg(not(has_std__ffi__c_char))]
+#[allow(non_camel_case_types)]
-+pub type c_char = i8;
++type c_char = i8;
+
+#[cfg(not(has_std__ffi__c_char))]
+#[allow(non_camel_case_types)]
-+pub type c_int = i32;
++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");
@@ contrib/libgit-rs/src/lib.rs (new)
+ }
+ }
+
++ /// 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<i32> {
+ let key = CString::new(key).expect("Couldn't convert to CString");
+ let mut val: c_int = 0;
@@ contrib/libgit-rs/src/lib.rs (new)
+ 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<String> {
+ let key = CString::new(key).expect("Couldn't convert key to CString");
+ let mut val: *mut c_char = std::ptr::null_mut();
@@ contrib/libgit-rs/src/lib.rs (new)
+ }
+}

+ ## contrib/libgit-rs/src/lib.rs (new) ##
+@@
++pub mod config;
+
## contrib/libgit-rs/testdata/config1 (new) ##
@@
+[trace2]
@@ contrib/libgit-sys/src/lib.rs: pub struct libgit_config_set {
pub fn libgit_user_agent() -> *const c_char; pub fn libgit_user_agent_sanitized() -> *const c_char;

+
+ ## t/Makefile ##
+@@ t/Makefile: 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

base-commit: 757161efcca150a9a96b312d9e780a071e601a03

Signature · 1 line
2.48.1.262.g85cc9f2d1e-goog
#189 lore raw

PATCH v7 1/4 common-main: split init and exit code into new files

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/[email protected]/
[2] https://lore.kernel.org/git/[email protected]/

---
 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
Makefile +2 -0
index 97e8385b66..27e68ac039 100644
@@ -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
common-exit.c new file +26 -0
new file mode 100644
index 0000000000..1aaa538be3
@@ -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;
+}
common-init.c new file +63 -0

63 lines not on this page · raw message

common-init.h new file +6 -0
new file mode 100644
index 0000000000..3e6db20cae
@@ -0,0 +1,6 @@
+#ifndef COMMON_INIT_H
+#define COMMON_INIT_H
+
+void init_git(const char **argv);
+
+#endif /* COMMON_INIT_H */
common-main.c +2 -81

94 lines not on this page · raw message

meson.build +2 -0
index 0064eb64f5..e5ba28b47f 100644
@@ -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',
Signature · 1 line
2.48.1.262.g85cc9f2d1e-goog
#190 lore raw

PATCH v7 2/4 libgit-sys: introduce Rust wrapper for libgit.a

In reply to Josh Steadmon

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.

---
 .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
.gitignore +1 -0
index e82aa19df0..31d7e64287 100644
@@ -250,3 +250,4 @@ Release/
 /git.VC.db
 *.dSYM
 /contrib/buildsystems/out
+/contrib/libgit-sys/target
Makefile +38 -0
index 27e68ac039..92989d3311 100644
@@ -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) $@ $^
contrib/libgit-sys/Cargo.lock new file +69 -0

69 lines not on this page · raw message

contrib/libgit-sys/Cargo.toml new file +19 -0
new file mode 100644
index 0000000000..e0623022c3
@@ -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/[email protected]/
+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"
contrib/libgit-sys/README.md new file +4 -0
new file mode 100644
index 0000000000..c061cfcaf5
@@ -0,0 +1,4 @@
+# libgit-sys
+
+A small proof-of-concept crate showing how to provide a Rust FFI to Git
+internals.
contrib/libgit-sys/build.rs new file +35 -0
new file mode 100644
index 0000000000..3ffd80ad91
@@ -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(())
+}
contrib/libgit-sys/public_symbol_export.c new file +22 -0
new file mode 100644
index 0000000000..cd1602206e
@@ -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
contrib/libgit-sys/public_symbol_export.h new file +8 -0
new file mode 100644
index 0000000000..a3372f93fa
@@ -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 */
contrib/libgit-sys/src/lib.rs new file +46 -0

46 lines not on this page · raw message

t/Makefile +10 -0
index daa5fcae86..53ba01c21b 100644
@@ -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
Signature · 1 line
2.48.1.262.g85cc9f2d1e-goog
#191 lore raw

Re: [PATCH v7 2/4] libgit-sys: introduce Rust wrapper for libgit.a

Quoted 4 lines · Josh Steadmon

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.

It's great to see some tests. This is looking good, I've left a couple of small comments below.

Makefile $(CLAR_TEST_PROG): $(UNIT_TEST_DIR)/clar.suite $(CLAR_TEST_OBJS) $(GITLIBS) GIT- · line 3921

Quoted 2 lines
Makefile +2 -0
+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 $@

This is a very long line and the ones below are pretty long - perhaps we could put the list of sources in a variable?

contrib/libgit-sys/public_symbol_export.c line 1

Quoted 13 lines
Makefile +5 -0
+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) $@ $^
[...]
contrib/libgit-sys/public_symbol_export.c new file +1 -0
new file mode 100644
index 0000000000..cd1602206e
@@ -0,0 +1,22 @@
+/* Shim to publicly export Git symbols. These must be renamed so that the

Style: Multiline comments start with an empty "/*" so this should be

/*
* Shim ...

contrib/libgit-sys/public_symbol_export.c line 4

Quoted 3 lines
contrib/libgit-sys/public_symbol_export.c new file +3 -0
+ * original symbols can be hidden. Renaming these with a "libgit_" prefix also
+ * avoids conflicts with other libraries such as libgit2.
+ */

Best Wishes

Phillip

Quoted 101 lines
contrib/libgit-sys/public_symbol_export.c new file +17 -0
+#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
contrib/libgit-sys/public_symbol_export.h new file +8 -0
new file mode 100644
index 0000000000..a3372f93fa
@@ -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 */
contrib/libgit-sys/src/lib.rs new file +46 -0
new file mode 100644
index 0000000000..d4853f3074
@@ -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
+        );
+    }
+}
t/Makefile +10 -0
index daa5fcae86..53ba01c21b 100644
@@ -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
#192 lore raw

Re: [PATCH v7 2/4] libgit-sys: introduce Rust wrapper for libgit.a

Quoted 8 lines · Phillip Wood
Quoted 4 lines · Josh Steadmon

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.

Quoted 8 lines

It's great to see some tests. This is looking good, I've left a couple of small comments below.

Makefile $(CLAR_TEST_PROG): $(UNIT_TEST_DIR)/clar.suite $(CLAR_TEST_OBJS) $(GITLIBS) GIT- · line 3921

Quoted 2 lines
Makefile +2 -0
+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 $@

This is a very long line and the ones below are pretty long - perhaps we could put the list of sources in a variable?

Done for V8.

Quoted 16 lines

contrib/libgit-sys/public_symbol_export.c line 1

Quoted 13 lines
Makefile +5 -0
+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) $@ $^
[...]
contrib/libgit-sys/public_symbol_export.c new file +1 -0
new file mode 100644
index 0000000000..cd1602206e
@@ -0,0 +1,22 @@
+/* Shim to publicly export Git symbols. These must be renamed so that the

Style: Multiline comments start with an empty "/*" so this should be

Quoted 2 lines

/*
* Shim ...

Whoops, fixed, thanks.

#193 lore raw

Re: [PATCH v7 2/4] libgit-sys: introduce Rust wrapper for libgit.a

Quoted 37 lines · Josh Steadmon
Quoted 8 lines · Phillip Wood
Quoted 4 lines · Josh Steadmon

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.

Quoted 8 lines

It's great to see some tests. This is looking good, I've left a couple of small comments below.

Makefile $(CLAR_TEST_PROG): $(UNIT_TEST_DIR)/clar.suite $(CLAR_TEST_OBJS) $(GITLIBS) GIT- · line 3921

Quoted 2 lines
Makefile +2 -0
+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 $@

This is a very long line and the ones below are pretty long - perhaps we could put the list of sources in a variable?

Done for V8.

Quoted 16 lines

contrib/libgit-sys/public_symbol_export.c line 1

Quoted 13 lines
Makefile +5 -0
+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) $@ $^
[...]
contrib/libgit-sys/public_symbol_export.c new file +1 -0
new file mode 100644
index 0000000000..cd1602206e
@@ -0,0 +1,22 @@
+/* Shim to publicly export Git symbols. These must be renamed so that the

Style: Multiline comments start with an empty "/*" so this should be

Quoted 4 lines
Quoted 2 lines

/*
* Shim ...

Whoops, fixed, thanks.

The series is looking good and it seems that we'll be expecting a hopefully small and final reroll? I'll mark the topic in the "What's cooking" draft as such.

Thanks, all.

#194 lore raw

PATCH v7 3/4 libgit-sys: also export some config_set functions

In reply to Josh Steadmon

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.

---
 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(-)
contrib/libgit-sys/public_symbol_export.c +29 -0
index cd1602206e..a0297cb1a5 100644
@@ -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();
contrib/libgit-sys/public_symbol_export.h +10 -0
index a3372f93fa..701db92d53 100644
@@ -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);
contrib/libgit-sys/src/lib.rs +30 -1
index d4853f3074..dadb4e5f40 100644
@@ -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)]
Signature · 1 line
2.48.1.262.g85cc9f2d1e-goog
#195 lore raw

Re: [PATCH v7 3/4] libgit-sys: also export some config_set functions

Hi Josh

Quoted 9 lines · Josh Steadmon

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.

This interface is looks nice, I've left a couple of comments below

contrib/libgit-sys/public_symbol_export.c line 11

Quoted 13 lines
contrib/libgit-sys/public_symbol_export.c +1 -0
index cd1602206e..a0297cb1a5 100644
@@ -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)

Personally I'd prefer it if we actually defined struct libgit_config_set here

struct libgit_config_set {
	struct config_set cs;
}

Then we could avoid all the casts below. For example

struct libgit_config_set *libgit_configset_alloc(void)

{
	struct libget_config_set *cs =
		xmalloc(sizeof(struct libgit_config_set));
	git_configset_init(&cs->cs);
	return cs;
}
Quoted 9 lines
contrib/libgit-sys/public_symbol_export.c +9 -0
+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)
+{

contrib/libgit-sys/public_symbol_export.c line 31

Quoted 10 lines
contrib/libgit-sys/public_symbol_export.c +10 -0
+	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)

Style: this and the one below could do with being wrapped at 80 characters

This whole series looks pretty good to me

Best Wishes

Phillip

Quoted 83 lines
contrib/libgit-sys/public_symbol_export.c +9 -0
+{
+	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();
contrib/libgit-sys/public_symbol_export.h +10 -0
index a3372f93fa..701db92d53 100644
@@ -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);
contrib/libgit-sys/src/lib.rs +30 -1
index d4853f3074..dadb4e5f40 100644
@@ -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)]
#196 lore raw

Re: [PATCH v7 3/4] libgit-sys: also export some config_set functions

Quoted 36 lines · Phillip Wood

Hi Josh

Quoted 9 lines · Josh Steadmon

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.

This interface is looks nice, I've left a couple of comments below

contrib/libgit-sys/public_symbol_export.c line 9

Quoted 11 lines
contrib/libgit-sys/public_symbol_export.c +1 -0
index cd1602206e..a0297cb1a5 100644
@@ -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)

Personally I'd prefer it if we actually defined struct libgit_config_set here

struct libgit_config_set {
	struct config_set cs;
}

Then we could avoid all the casts below. For example

Quoted 7 lines

struct libgit_config_set *libgit_configset_alloc(void)

{
	struct libget_config_set *cs =
		xmalloc(sizeof(struct libgit_config_set));
	git_configset_init(&cs->cs);
	return cs;
}

Hmm yeah I remember this feedback from (checks Lore) back in V2. I think you're right, we should have gone this way from the beginning. Done in V8.

Quoted 13 lines
Quoted 13 lines
contrib/libgit-sys/public_symbol_export.c +13 -0
+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);
+}
+
Quoted 8 lines

contrib/libgit-sys/public_symbol_export.c line 31

Quoted 6 lines
contrib/libgit-sys/public_symbol_export.c +6 -0
+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)

Style: this and the one below could do with being wrapped at 80 characters

Fixed.

Quoted 5 lines

This whole series looks pretty good to me

Best Wishes

Phillip

#197 lore raw

PATCH v7 4/4 libgit: add higher-level libgit crate

In reply to Josh Steadmon

From: Calvin Wan <[email protected]>

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

---
 .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
.gitignore +1 -0
index 31d7e64287..acdd8ce7c7 100644
@@ -250,4 +250,5 @@ Release/
 /git.VC.db
 *.dSYM
 /contrib/buildsystems/out
+/contrib/libgit-rs/target
 /contrib/libgit-sys/target
Makefile +9 -4
index 92989d3311..37b63de079 100644
@@ -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
contrib/libgit-rs/Cargo.lock new file +77 -0

77 lines not on this page · raw message

contrib/libgit-rs/Cargo.toml new file +17 -0
new file mode 100644
index 0000000000..c3289e69db
@@ -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/[email protected]/
+
+
+[lib]
+path = "src/lib.rs"
+
+[dependencies]
+libgit-sys = { version = "0.1.0", path = "../libgit-sys" }
+
+[build-dependencies]
+autocfg = "1.4.0"
contrib/libgit-rs/README.md new file +13 -0
new file mode 100644
index 0000000000..ff945e1ce2
@@ -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).
contrib/libgit-rs/build.rs new file +4 -0
new file mode 100644
index 0000000000..f8bd01a690
@@ -0,0 +1,4 @@
+pub fn main() {
+    let ac = autocfg::new();
+    ac.emit_has_path("std::ffi::c_char");
+}
contrib/libgit-rs/src/config.rs new file +106 -0

106 lines not on this page · raw message

contrib/libgit-rs/src/lib.rs new file +1 -0
new file mode 100644
index 0000000000..ef68c36943
@@ -0,0 +1 @@
+pub mod config;
contrib/libgit-rs/testdata/config1 new file +2 -0
new file mode 100644
index 0000000000..4e9a9d25d1
@@ -0,0 +1,2 @@
+[trace2]
+	eventNesting = 1
contrib/libgit-rs/testdata/config2 new file +2 -0
new file mode 100644
index 0000000000..b8d1eca423
@@ -0,0 +1,2 @@
+[trace2]
+	eventTarget = 1
contrib/libgit-rs/testdata/config3 new file +2 -0
new file mode 100644
index 0000000000..ca7b9a7c38
@@ -0,0 +1,2 @@
+[trace2]
+	eventNesting = 3
contrib/libgit-sys/src/lib.rs +4 -0
index dadb4e5f40..4bfc650450 100644
@@ -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;
 
t/Makefile +7 -2
index 53ba01c21b..2994eb5fa9 100644
@@ -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
Signature · 1 line
2.48.1.262.g85cc9f2d1e-goog
#198 lore raw

Re: [PATCH v7 4/4] libgit: add higher-level libgit crate

Quoted 6 lines · Josh Steadmon

-.PHONY: libgit-sys
+.PHONY: libgit-sys libgit-rs
libgit-sys:

Makefile +0 -0
 	$(QUIET)(\
 		cd contrib/libgit-sys && \
 		cargo build \

Makefile build-unit-tests: $(UNIT_TEST_PROGS) $(CLAR_TEST_PROG) · line 3923

Quoted 10 lines
Makefile +6 -1
 	)
+libgit-rs:
+	$(QUIET)(\
+		cd contrib/libgit-rs && \
+		cargo build \
+	)
 ifdef INCLUDE_LIBGIT_RS
-all:: libgit-sys
+all:: libgit-sys libgit-rs
 endif

I somehow would have expected this part of the patch to do

	libgit-sys libgit-rs:
		$(QUIET)( \
			cd contrib/$@ && cargo build \
		)

but the above longhand is fine.

contrib/libgit-rs/src/config.rs line 52

Quoted 13 lines
contrib/libgit-rs/src/config.rs new file +13 -0
+    /// 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<i32> {
+        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())
+    }

Nice.

I was wondering why libgit_configset_get_int() in [3/4] does not do better than just wrapping the raw C interface, which returns the error/success status separately from the value we parsed. With this higher-layer wrapper around it, the interface becomes a bit more like a higher-level language.

Thanks, will queue.

#199 lore raw

Re: [PATCH v7 4/4] libgit: add higher-level libgit crate

Quoted 21 lines · Junio C Hamano
Quoted 6 lines · Josh Steadmon

-.PHONY: libgit-sys
+.PHONY: libgit-sys libgit-rs
libgit-sys:

Makefile +0 -0
 	$(QUIET)(\
 		cd contrib/libgit-sys && \
 		cargo build \

Makefile build-unit-tests: $(UNIT_TEST_PROGS) $(CLAR_TEST_PROG) · line 3923

Quoted 10 lines
Makefile +6 -1
 	)
+libgit-rs:
+	$(QUIET)(\
+		cd contrib/libgit-rs && \
+		cargo build \
+	)
 ifdef INCLUDE_LIBGIT_RS
-all:: libgit-sys
+all:: libgit-sys libgit-rs
 endif

I somehow would have expected this part of the patch to do

Quoted 6 lines
	libgit-sys libgit-rs:
		$(QUIET)( \
			cd contrib/$@ && cargo build \
		)

but the above longhand is fine.

Fixed since I'm sending out a V8 for Phillip's feedback anyway. Thanks.

Quoted 23 lines

contrib/libgit-rs/src/config.rs line 52

Quoted 13 lines
contrib/libgit-rs/src/config.rs new file +13 -0
+    /// 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<i32> {
+        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())
+    }

Nice.

I was wondering why libgit_configset_get_int() in [3/4] does not do better than just wrapping the raw C interface, which returns the error/success status separately from the value we parsed. With this higher-layer wrapper around it, the interface becomes a bit more like a higher-level language.

Thanks, will queue.

#200 lore raw

Re: [PATCH v7 4/4] libgit: add higher-level libgit crate

Quoted 10 lines · Josh Steadmon
Quoted 8 lines

I somehow would have expected this part of the patch to do

	libgit-sys libgit-rs:
		$(QUIET)( \
			cd contrib/$@ && cargo build \
		)

but the above longhand is fine.

Fixed since I'm sending out a V8 for Phillip's feedback anyway. Thanks.

It would be a "fix" only if we anticipate that we will add in the future more Rust things to contrib/ and all of them would follow the same pattern. Otherwise, it does not really matter, and as I said, what you posted is just fine.

Thanks.

#201 lore raw

v8 cover Introduce libgit-rs, a Rust wrapper around libgit.a

In reply to Josh Steadmon

This series provides two small Rust wrapper libraries around parts of
Git: "libgit-sys", which exposes a few functions from libgit.a, and "libgit", which provides a more Rust-friendly interface to some of those functions. In addition to included unit tests, at $DAYJOB we have tested building JJ[1] with our library and used it to replace some of the libgit2-rs uses.

[1] https://github.com/jj-vcs/jj

There is known NEEDSWORK, but I feel that they can be addressed in follow-up changes, rather than in this series. If you feel otherwise, please let me know:

* Investigate alternative methods of managing symbol visibility & renaming.

* Figure out symbol versioning

Changes in V8:
* Define a private libgit_config_set struct to avoid excessive casting in public_symbol_export.c.

* Style fixes: merge some Makefile rules, limit rule line length by defining intermediate variables, add initial empty comment line, add linebreaks in function definitions.

Changes in V7:
* Moved the ConfigSet implementation in libgit-rs to a config module.

* Added doc comments for ConfigSet and its methods.

* Fix meson builds by adding new object files to libgit_sources

* Moved INCLUDE_LIBGIT_RS Makefile changes earlier in the series, so that we can make it optional to compile some of the libgitpub sources. Squashed V6 patch 5/5 into this series' patch 4/4.

* Don't publicly export FFI types in libgit-rs.

* Removed extraneous -r argument to $(RM) in the clean rules.

* Added TODO reminder in Cargo.toml about removing Cargo.lock once we hit a certain minimum supported Rust version.

* Style cleanup in public_symbol_export.c

Changes in V6:
* Rebased onto current master, since V5 was several months old.

* Move libgit-sys out of libgit-rs; while this sort of nesting is common in Rust crates with standalone repositories, it doesn't make as much sense when they're contained in the larger Git project's repo.

* Standardize the naming of some of the Makefile targets to always include a dash in the "-rs" or "-sys" suffixes.

* Clean up READMEs and crate descriptions in preparation for uploading to crates.io.

Changes in V5:
* When building with INCLUDE_LIBGIT_RS defined, add "-fvisibility=hidden" to CFLAGS. This allows us to manage symbol visibility in libgitpub.a without causing make all to rebuild from scratch due to changing CFLAGS.

* Avoid using c_int in the higher-level Rust API.

* Remove libgitpub.a and intermediate files with make clean.

Changes in V4:
* Drop V3 patch #3, which added wrappers around repository initialization and config access. These are not well-libified, and they are not necessary for JJ's proof-of-concept use case, so let's avoid exporting them for now.

* Set a minimum supported Rust version of 1.63. Autodetect whether our Rust version has c_int and c_char types; if not, define them ourselves.

* When building libgitpub.a via build.rs, set DEVELOPER=1 to catch additional errors at build time.

* In build.rs, use the make_cmd crate to portable select the correct invocation of GNU Make.

* Follow naming standards for _alloc() and _free() functions.

* Use String instead of CString in higher-level API.

* Move libgit_configset_alloc() and libgit_configset_free() out of upstream Git, to the libgitpub shim library.

* In libgitpub, initialize libgit_config_set structs in the _alloc() function rather than with a separate _init() function.

* Remove unnecessary comments in libgit-sys showing where the wrapped functions were originally defined.

* Fix clippy lint: don't reborrow configfile path references.

* Various typo fixes and cargo fmt fixes.

Changes in V3:
* Renamed cgit-rs to libgit-rs and cgit-sys to libgit-sys

* Makefile cleanup, particularly adding config.mak options that developers can set to run Rust builds and tests by default (Patch 6)

* Provide testdata configs for unit tests

* ConfigSet API now uses &Path instead of &str -- more ergonomic for Rust users to pass in and errors out if the path string isn't UTF-8

* Fixed unresolved dependency on libz in Cargo.toml

Calvin Wan (1):
libgit: add higher-level libgit crate

Josh Steadmon (3):
common-main: split init and exit code into new files
libgit-sys: introduce Rust wrapper for libgit.a
libgit-sys: also export some config_set functions

 .gitignore                                |   2 +
 Makefile                                  |  49 ++++++++++
 common-exit.c                             |  26 ++++++
 common-init.c                             |  63 +++++++++++++
 common-init.h                             |   6 ++
 common-main.c                             |  83 +----------------
 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/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 |  59 ++++++++++++
 contrib/libgit-sys/public_symbol_export.h |  18 ++++
 contrib/libgit-sys/src/lib.rs             |  79 ++++++++++++++++
 meson.build                               |   2 +
 t/Makefile                                |  15 +++
 24 files changed, 672 insertions(+), 81 deletions(-)
 create mode 100644 common-exit.c
 create mode 100644 common-init.c
 create mode 100644 common-init.h
 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
 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

Range-diff against v7:

1:  cd0cb9aa04 = 1:  cd0cb9aa04 common-main: split init and exit code into new files
2:  f1502b8590 ! 2:  3588a3c3fc libgit-sys: introduce Rust wrapper for libgit.a
    @@ Makefile: $(CLAR_TEST_PROG): $(UNIT_TEST_DIR)/clar.suite $(CLAR_TEST_OBJS) $(GIT
     +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
    ++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 $@
     +
    -+contrib/libgit-sys/hidden_symbol_export.o: contrib/libgit-sys/partial_symbol_export.o
    ++$(LIBGIT_HIDDEN_EXPORT): $(LIBGIT_PARTIAL_EXPORT)
     +	$(OBJCOPY) --localize-hidden $^ $@
     +
    -+contrib/libgit-sys/libgitpub.a: contrib/libgit-sys/hidden_symbol_export.o
    ++contrib/libgit-sys/libgitpub.a: $(LIBGIT_HIDDEN_EXPORT)
     +	$(AR) $(ARFLAGS) $@ $^
     
      ## contrib/libgit-sys/Cargo.lock (new) ##
    @@ contrib/libgit-sys/build.rs (new)
     
      ## contrib/libgit-sys/public_symbol_export.c (new) ##
     @@
    -+/* Shim to publicly export Git symbols. These must be renamed so that the
    ++/*
    ++ * 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.
     + */
3:  d67d3648d1 ! 3:  f4452fffe6 libgit-sys: also export some config_set functions
    @@ contrib/libgit-sys/public_symbol_export.c
      
      #pragma GCC visibility push(default)
      
    ++struct libgit_config_set {
    ++	struct config_set cs;
    ++};
    ++
     +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;
    ++	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((struct config_set *) cs);
    -+	free((struct 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((struct config_set *) cs, filename);
    ++	return git_configset_add_file(&cs->cs, filename);
     +}
     +
    -+int libgit_configset_get_int(struct libgit_config_set *cs, const char *key, int *dest)
    ++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);
    ++	return git_configset_get_int(&cs->cs, key, dest);
     +}
     +
    -+int libgit_configset_get_string(struct libgit_config_set *cs, const char *key, char **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);
    ++	return git_configset_get_string(&cs->cs, key, dest);
     +}
     +
      const char *libgit_user_agent(void)
4:  88425bb0b1 ! 4:  ada9fc0a13 libgit: add higher-level libgit crate
    @@ Makefile: build-unit-tests: $(UNIT_TEST_PROGS) $(CLAR_TEST_PROG)
      	$(MAKE) -C t/ unit-tests
      
     -.PHONY: libgit-sys
    +-libgit-sys:
     +.PHONY: libgit-sys libgit-rs
    - libgit-sys:
    ++libgit-sys libgit-rs:
      	$(QUIET)(\
    - 		cd contrib/libgit-sys && \
    +-		cd contrib/libgit-sys && \
    ++		cd contrib/$@ && \
      		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
    + LIBGIT_PUB_OBJS = contrib/libgit-sys/public_symbol_export.o
     
      ## contrib/libgit-rs/Cargo.lock (new) ##
     @@

base-commit: 757161efcca150a9a96b312d9e780a071e601a03

Signature · 1 line
2.48.1.262.g85cc9f2d1e-goog
#202 lore raw

PATCH v8 1/4 common-main: split init and exit code into new files

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/[email protected]/
[2] https://lore.kernel.org/git/[email protected]/

---
 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
Makefile +2 -0
index 97e8385b66..27e68ac039 100644
@@ -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
common-exit.c new file +26 -0
new file mode 100644
index 0000000000..1aaa538be3
@@ -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;
+}
common-init.c new file +63 -0

63 lines not on this page · raw message

common-init.h new file +6 -0
new file mode 100644
index 0000000000..3e6db20cae
@@ -0,0 +1,6 @@
+#ifndef COMMON_INIT_H
+#define COMMON_INIT_H
+
+void init_git(const char **argv);
+
+#endif /* COMMON_INIT_H */
common-main.c +2 -81

94 lines not on this page · raw message

meson.build +2 -0
index 0064eb64f5..e5ba28b47f 100644
@@ -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',
Signature · 1 line
2.48.1.262.g85cc9f2d1e-goog
#203 lore raw

PATCH v8 2/4 libgit-sys: introduce Rust wrapper for libgit.a

In reply to Josh Steadmon

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.

---
 .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
.gitignore +1 -0
index e82aa19df0..31d7e64287 100644
@@ -250,3 +250,4 @@ Release/
 /git.VC.db
 *.dSYM
 /contrib/buildsystems/out
+/contrib/libgit-sys/target
Makefile +47 -0

80 lines not on this page · raw message

contrib/libgit-sys/Cargo.lock new file +69 -0

69 lines not on this page · raw message

contrib/libgit-sys/Cargo.toml new file +19 -0
new file mode 100644
index 0000000000..e0623022c3
@@ -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/[email protected]/
+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"
contrib/libgit-sys/README.md new file +4 -0
new file mode 100644
index 0000000000..c061cfcaf5
@@ -0,0 +1,4 @@
+# libgit-sys
+
+A small proof-of-concept crate showing how to provide a Rust FFI to Git
+internals.
contrib/libgit-sys/build.rs new file +35 -0
new file mode 100644
index 0000000000..3ffd80ad91
@@ -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(())
+}
contrib/libgit-sys/public_symbol_export.c new file +23 -0
new file mode 100644
index 0000000000..4c153c6f0d
@@ -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
contrib/libgit-sys/public_symbol_export.h new file +8 -0
new file mode 100644
index 0000000000..a3372f93fa
@@ -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 */
contrib/libgit-sys/src/lib.rs new file +46 -0

46 lines not on this page · raw message

t/Makefile +10 -0
index daa5fcae86..53ba01c21b 100644
@@ -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
Signature · 1 line
2.48.1.262.g85cc9f2d1e-goog
#204 lore raw

Re: [PATCH v8 2/4] libgit-sys: introduce Rust wrapper for libgit.a

Quoted 15 lines · Josh Steadmon
Makefile +3 -0
index 27e68ac039..f2c08df4cb 100644
@@ -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.

Makefile CURL_CONFIG = curl-config · line 666

Quoted 7 lines
Makefile +2 -0
@@ -657,6 +660,8 @@ CURL_CONFIG = curl-config
 GCOV = gcov
 STRIP = strip
 SPATCH = spatch
+LD = ld
+OBJCOPY = objcopy
 
 export TCL_PATH TCLTK_PATH

Good.

Makefile $(CLAR_TEST_PROG): $(UNIT_TEST_DIR)/clar.suite $(CLAR_TEST_OBJS) $(GITLIBS) GIT- · line 3923

Quoted 4 lines
Makefile +4 -0
+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

This is a fairly minor point, but if we look at the places where OBJECTS, PROGRAMS, and friends are prepared in the Makefile, they all start with empty, i.e.

LIBGIT_PUB_OBJS =

at a fairly early place in the file, and then appends real contents to the symbol with += assignment. It probably makes sense to follow suit, although the result would be the same.

Other than that, nothing jumped at me as fishy. Nicely done.

Thanks. Will replace.

#205 lore raw

Re: [PATCH v8 2/4] libgit-sys: introduce Rust wrapper for libgit.a

Quoted 38 lines · Junio C Hamano
Quoted 15 lines · Josh Steadmon
Makefile +3 -0
index 27e68ac039..f2c08df4cb 100644
@@ -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.

Makefile CURL_CONFIG = curl-config · line 666

Quoted 7 lines
Makefile +2 -0
@@ -657,6 +660,8 @@ CURL_CONFIG = curl-config
 GCOV = gcov
 STRIP = strip
 SPATCH = spatch
+LD = ld
+OBJCOPY = objcopy
 
 export TCL_PATH TCLTK_PATH

Good.

Makefile $(CLAR_TEST_PROG): $(UNIT_TEST_DIR)/clar.suite $(CLAR_TEST_OBJS) $(GITLIBS) GIT- · line 3923

Quoted 4 lines
Makefile +4 -0
+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

This is a fairly minor point, but if we look at the places where OBJECTS, PROGRAMS, and friends are prepared in the Makefile, they all start with empty, i.e.

LIBGIT_PUB_OBJS =

Quoted 7 lines

at a fairly early place in the file, and then appends real contents to the symbol with += assignment. It probably makes sense to follow suit, although the result would be the same.

Other than that, nothing jumped at me as fishy. Nicely done.

Thanks. Will replace.

Ack, will send a V9 soon with this & a fix for Phillip's feedback. Thanks.

#206 lore raw

PATCH v8 3/4 libgit-sys: also export some config_set functions

In reply to Josh Steadmon

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.

---
 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(-)
contrib/libgit-sys/public_symbol_export.c +36 -0
index 4c153c6f0d..dbb7948104 100644
@@ -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();
contrib/libgit-sys/public_symbol_export.h +10 -0
index a3372f93fa..701db92d53 100644
@@ -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);
contrib/libgit-sys/src/lib.rs +30 -1
index d4853f3074..dadb4e5f40 100644
@@ -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)]
Signature · 1 line
2.48.1.262.g85cc9f2d1e-goog
#207 lore raw

PATCH v8 4/4 libgit: add higher-level libgit crate

In reply to Josh Steadmon

From: Calvin Wan <[email protected]>

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

---
 .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
.gitignore +1 -0
index 31d7e64287..acdd8ce7c7 100644
@@ -250,4 +250,5 @@ Release/
 /git.VC.db
 *.dSYM
 /contrib/buildsystems/out
+/contrib/libgit-rs/target
 /contrib/libgit-sys/target
Makefile +6 -6
index f2c08df4cb..f883d310aa 100644
@@ -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
contrib/libgit-rs/Cargo.lock new file +77 -0

77 lines not on this page · raw message

contrib/libgit-rs/Cargo.toml new file +17 -0
new file mode 100644
index 0000000000..c3289e69db
@@ -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/[email protected]/
+
+
+[lib]
+path = "src/lib.rs"
+
+[dependencies]
+libgit-sys = { version = "0.1.0", path = "../libgit-sys" }
+
+[build-dependencies]
+autocfg = "1.4.0"
contrib/libgit-rs/README.md new file +13 -0
new file mode 100644
index 0000000000..ff945e1ce2
@@ -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).
contrib/libgit-rs/build.rs new file +4 -0
new file mode 100644
index 0000000000..f8bd01a690
@@ -0,0 +1,4 @@
+pub fn main() {
+    let ac = autocfg::new();
+    ac.emit_has_path("std::ffi::c_char");
+}
contrib/libgit-rs/src/config.rs new file +106 -0

106 lines not on this page · raw message

contrib/libgit-rs/src/lib.rs new file +1 -0
new file mode 100644
index 0000000000..ef68c36943
@@ -0,0 +1 @@
+pub mod config;
contrib/libgit-rs/testdata/config1 new file +2 -0
new file mode 100644
index 0000000000..4e9a9d25d1
@@ -0,0 +1,2 @@
+[trace2]
+	eventNesting = 1
contrib/libgit-rs/testdata/config2 new file +2 -0
new file mode 100644
index 0000000000..b8d1eca423
@@ -0,0 +1,2 @@
+[trace2]
+	eventTarget = 1
contrib/libgit-rs/testdata/config3 new file +2 -0
new file mode 100644
index 0000000000..ca7b9a7c38
@@ -0,0 +1,2 @@
+[trace2]
+	eventNesting = 3
contrib/libgit-sys/src/lib.rs +4 -0
index dadb4e5f40..4bfc650450 100644
@@ -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;
 
t/Makefile +7 -2
index 53ba01c21b..2994eb5fa9 100644
@@ -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
Signature · 1 line
2.48.1.262.g85cc9f2d1e-goog
#208 lore raw

Re: [PATCH v8 0/4] Introduce libgit-rs, a Rust wrapper around libgit.a

In reply to Josh Steadmon

Hi Josh

On 28/01/2025 22:01, Josh Steadmon wrote:

Thanks for re-rolling, the range-diff looks good to me apart from

Quoted 6 lines
      +void libgit_configset_free(struct libgit_config_set *cs)
      +{
     -+	git_configset_clear((struct config_set *) cs);
     -+	free((struct config_set *) cs);
     ++	git_configset_clear(&cs->cs);
     ++	free(&cs->cs);

Which I think should be "free(cs)". In practice it does not matter because we pass the same value to free() but it seems a bit odd to pass the address of the first member of the struct rather than the address of the struct itself.

I'm looking forward to seeing this merged soon

Best Wishes

Phillip

#209 lore raw

Re: [PATCH v8 0/4] Introduce libgit-rs, a Rust wrapper around libgit.a

Quoted 13 lines · Phillip Wood

Hi Josh

On 28/01/2025 22:01, Josh Steadmon wrote:

Thanks for re-rolling, the range-diff looks good to me apart from

Quoted 6 lines
      +void libgit_configset_free(struct libgit_config_set *cs)
      +{
     -+	git_configset_clear((struct config_set *) cs);
     -+	free((struct config_set *) cs);
     ++	git_configset_clear(&cs->cs);
     ++	free(&cs->cs);
Quoted 4 lines

Which I think should be "free(cs)". In practice it does not matter because we pass the same value to free() but it seems a bit odd to pass the address of the first member of the struct rather than the address of the struct itself.

Yep sorry, got a bit careless with search-and-replace. Thanks for the catch!

Quoted 6 lines

I'm looking forward to seeing this merged soon

Best Wishes

Phillip

#210 lore raw

v9 cover Introduce libgit-rs, a Rust wrapper around libgit.a

In reply to Josh Steadmon

This series provides two small Rust wrapper libraries around parts of
Git: "libgit-sys", which exposes a few functions from libgit.a, and "libgit", which provides a more Rust-friendly interface to some of those functions. In addition to included unit tests, at $DAYJOB we have tested building JJ[1] with our library and used it to replace some of the libgit2-rs uses.

[1] https://github.com/jj-vcs/jj

There is known NEEDSWORK, but I feel that they can be addressed in follow-up changes, rather than in this series. If you feel otherwise, please let me know:

* Investigate alternative methods of managing symbol visibility & renaming.

* Figure out symbol versioning

Changes in V9:
* Properly initialize Makefile var LIBGIT_PUB_OBJS.

* Pass the correct pointer to free in libgit_configset_free().

Changes in V8:
* Define a private libgit_config_set struct to avoid excessive casting in public_symbol_export.c.

* Style fixes: merge some Makefile rules, limit rule line length by defining intermediate variables, add initial empty comment line, add linebreaks in function definitions.

Changes in V7:
* Moved the ConfigSet implementation in libgit-rs to a config module.

* Added doc comments for ConfigSet and its methods.

* Fix meson builds by adding new object files to libgit_sources

* Moved INCLUDE_LIBGIT_RS Makefile changes earlier in the series, so that we can make it optional to compile some of the libgitpub sources. Squashed V6 patch 5/5 into this series' patch 4/4.

* Don't publicly export FFI types in libgit-rs.

* Removed extraneous -r argument to $(RM) in the clean rules.

* Added TODO reminder in Cargo.toml about removing Cargo.lock once we hit a certain minimum supported Rust version.

* Style cleanup in public_symbol_export.c

Changes in V6:
* Rebased onto current master, since V5 was several months old.

* Move libgit-sys out of libgit-rs; while this sort of nesting is common in Rust crates with standalone repositories, it doesn't make as much sense when they're contained in the larger Git project's repo.

* Standardize the naming of some of the Makefile targets to always include a dash in the "-rs" or "-sys" suffixes.

* Clean up READMEs and crate descriptions in preparation for uploading to crates.io.

Changes in V5:
* When building with INCLUDE_LIBGIT_RS defined, add "-fvisibility=hidden" to CFLAGS. This allows us to manage symbol visibility in libgitpub.a without causing make all to rebuild from scratch due to changing CFLAGS.

* Avoid using c_int in the higher-level Rust API.

* Remove libgitpub.a and intermediate files with make clean.

Changes in V4:
* Drop V3 patch #3, which added wrappers around repository initialization and config access. These are not well-libified, and they are not necessary for JJ's proof-of-concept use case, so let's avoid exporting them for now.

* Set a minimum supported Rust version of 1.63. Autodetect whether our Rust version has c_int and c_char types; if not, define them ourselves.

* When building libgitpub.a via build.rs, set DEVELOPER=1 to catch additional errors at build time.

* In build.rs, use the make_cmd crate to portable select the correct invocation of GNU Make.

* Follow naming standards for _alloc() and _free() functions.

* Use String instead of CString in higher-level API.

* Move libgit_configset_alloc() and libgit_configset_free() out of upstream Git, to the libgitpub shim library.

* In libgitpub, initialize libgit_config_set structs in the _alloc() function rather than with a separate _init() function.

* Remove unnecessary comments in libgit-sys showing where the wrapped functions were originally defined.

* Fix clippy lint: don't reborrow configfile path references.

* Various typo fixes and cargo fmt fixes.

Changes in V3:
* Renamed cgit-rs to libgit-rs and cgit-sys to libgit-sys

* Makefile cleanup, particularly adding config.mak options that developers can set to run Rust builds and tests by default (Patch 6)

* Provide testdata configs for unit tests

* ConfigSet API now uses &Path instead of &str -- more ergonomic for Rust users to pass in and errors out if the path string isn't UTF-8

* Fixed unresolved dependency on libz in Cargo.toml

Calvin Wan (1):
libgit: add higher-level libgit crate

Josh Steadmon (3):
common-main: split init and exit code into new files
libgit-sys: introduce Rust wrapper for libgit.a
libgit-sys: also export some config_set functions

 .gitignore                                |   2 +
 Makefile                                  |  50 ++++++++++
 common-exit.c                             |  26 ++++++
 common-init.c                             |  63 +++++++++++++
 common-init.h                             |   6 ++
 common-main.c                             |  83 +----------------
 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/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 |  59 ++++++++++++
 contrib/libgit-sys/public_symbol_export.h |  18 ++++
 contrib/libgit-sys/src/lib.rs             |  79 ++++++++++++++++
 meson.build                               |   2 +
 t/Makefile                                |  15 +++
 24 files changed, 673 insertions(+), 81 deletions(-)
 create mode 100644 common-exit.c
 create mode 100644 common-init.c
 create mode 100644 common-init.h
 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
 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

Range-diff against v8:

1:  cd0cb9aa04 = 1:  cd0cb9aa04 common-main: split init and exit code into new files
2:  3588a3c3fc ! 2:  8793ff64a7 libgit-sys: introduce Rust wrapper for libgit.a
    @@ Makefile: $(CLAR_TEST_PROG): $(UNIT_TEST_DIR)/clar.suite $(CLAR_TEST_OBJS) $(GIT
     +all:: libgit-sys
     +endif
     +
    -+LIBGIT_PUB_OBJS = contrib/libgit-sys/public_symbol_export.o
    ++LIBGIT_PUB_OBJS =
    ++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
3:  f4452fffe6 ! 3:  ab32bd1d07 libgit-sys: also export some config_set functions
    @@ contrib/libgit-sys/public_symbol_export.c
     +void libgit_configset_free(struct libgit_config_set *cs)
     +{
     +	git_configset_clear(&cs->cs);
    -+	free(&cs->cs);
    ++	free(cs);
     +}
     +
     +int libgit_configset_add_file(struct libgit_config_set *cs, const char *filename)

4: ada9fc0a13 ! 4: 1bf8c5392c libgit: add higher-level libgit crate
@@ Makefile: build-unit-tests: $(UNIT_TEST_PROGS) $(CLAR_TEST_PROG)
+all:: libgit-sys libgit-rs
endif

- LIBGIT_PUB_OBJS = contrib/libgit-sys/public_symbol_export.o
+ LIBGIT_PUB_OBJS =

## contrib/libgit-rs/Cargo.lock (new) ##
@@

base-commit: 757161efcca150a9a96b312d9e780a071e601a03

Signature · 1 line
2.48.1.362.g079036d154-goog
#211 lore raw

PATCH v9 1/4 common-main: split init and exit code into new files

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/[email protected]/
[2] https://lore.kernel.org/git/[email protected]/

---
 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
Makefile +2 -0
index 97e8385b66..27e68ac039 100644
@@ -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
common-exit.c new file +26 -0
new file mode 100644
index 0000000000..1aaa538be3
@@ -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;
+}
common-init.c new file +63 -0

63 lines not on this page · raw message

common-init.h new file +6 -0
new file mode 100644
index 0000000000..3e6db20cae
@@ -0,0 +1,6 @@
+#ifndef COMMON_INIT_H
+#define COMMON_INIT_H
+
+void init_git(const char **argv);
+
+#endif /* COMMON_INIT_H */
common-main.c +2 -81

94 lines not on this page · raw message

meson.build +2 -0
index 0064eb64f5..e5ba28b47f 100644
@@ -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',
Signature · 1 line
2.48.1.362.g079036d154-goog
#212 lore raw

PATCH v9 2/4 libgit-sys: introduce Rust wrapper for libgit.a

In reply to Josh Steadmon

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.

---
 .gitignore                                |  1 +
 Makefile                                  | 48 ++++++++++++++++
 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, 263 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
.gitignore +1 -0
index e82aa19df0..31d7e64287 100644
@@ -250,3 +250,4 @@ Release/
 /git.VC.db
 *.dSYM
 /contrib/buildsystems/out
+/contrib/libgit-sys/target
Makefile +48 -0

81 lines not on this page · raw message

contrib/libgit-sys/Cargo.lock new file +69 -0

69 lines not on this page · raw message

contrib/libgit-sys/Cargo.toml new file +19 -0
new file mode 100644
index 0000000000..e0623022c3
@@ -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/[email protected]/
+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"
contrib/libgit-sys/README.md new file +4 -0
new file mode 100644
index 0000000000..c061cfcaf5
@@ -0,0 +1,4 @@
+# libgit-sys
+
+A small proof-of-concept crate showing how to provide a Rust FFI to Git
+internals.
contrib/libgit-sys/build.rs new file +35 -0
new file mode 100644
index 0000000000..3ffd80ad91
@@ -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(())
+}
contrib/libgit-sys/public_symbol_export.c new file +23 -0
new file mode 100644
index 0000000000..4c153c6f0d
@@ -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
contrib/libgit-sys/public_symbol_export.h new file +8 -0
new file mode 100644
index 0000000000..a3372f93fa
@@ -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 */
contrib/libgit-sys/src/lib.rs new file +46 -0

46 lines not on this page · raw message

t/Makefile +10 -0
index daa5fcae86..53ba01c21b 100644
@@ -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
Signature · 1 line
2.48.1.362.g079036d154-goog
#213 lore raw

PATCH v9 3/4 libgit-sys: also export some config_set functions

In reply to Josh Steadmon

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.

---
 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(-)
contrib/libgit-sys/public_symbol_export.c +36 -0
index 4c153c6f0d..dfbb257115 100644
@@ -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);
+}
+
+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();
contrib/libgit-sys/public_symbol_export.h +10 -0
index a3372f93fa..701db92d53 100644
@@ -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);
contrib/libgit-sys/src/lib.rs +30 -1
index d4853f3074..dadb4e5f40 100644
@@ -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)]
Signature · 1 line
2.48.1.362.g079036d154-goog
#214 lore raw

PATCH v9 4/4 libgit: add higher-level libgit crate

In reply to Josh Steadmon

From: Calvin Wan <[email protected]>

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

---
 .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
.gitignore +1 -0
index 31d7e64287..acdd8ce7c7 100644
@@ -250,4 +250,5 @@ Release/
 /git.VC.db
 *.dSYM
 /contrib/buildsystems/out
+/contrib/libgit-rs/target
 /contrib/libgit-sys/target
Makefile +6 -6
index a772bff186..7681ec9a3d 100644
@@ -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-rs/Cargo.lock new file +77 -0

77 lines not on this page · raw message

contrib/libgit-rs/Cargo.toml new file +17 -0
new file mode 100644
index 0000000000..c3289e69db
@@ -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/[email protected]/
+
+
+[lib]
+path = "src/lib.rs"
+
+[dependencies]
+libgit-sys = { version = "0.1.0", path = "../libgit-sys" }
+
+[build-dependencies]
+autocfg = "1.4.0"
contrib/libgit-rs/README.md new file +13 -0
new file mode 100644
index 0000000000..ff945e1ce2
@@ -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).
contrib/libgit-rs/build.rs new file +4 -0
new file mode 100644
index 0000000000..f8bd01a690
@@ -0,0 +1,4 @@
+pub fn main() {
+    let ac = autocfg::new();
+    ac.emit_has_path("std::ffi::c_char");
+}
contrib/libgit-rs/src/config.rs new file +106 -0

106 lines not on this page · raw message

contrib/libgit-rs/src/lib.rs new file +1 -0
new file mode 100644
index 0000000000..ef68c36943
@@ -0,0 +1 @@
+pub mod config;
contrib/libgit-rs/testdata/config1 new file +2 -0
new file mode 100644
index 0000000000..4e9a9d25d1
@@ -0,0 +1,2 @@
+[trace2]
+	eventNesting = 1
contrib/libgit-rs/testdata/config2 new file +2 -0
new file mode 100644
index 0000000000..b8d1eca423
@@ -0,0 +1,2 @@
+[trace2]
+	eventTarget = 1
contrib/libgit-rs/testdata/config3 new file +2 -0
new file mode 100644
index 0000000000..ca7b9a7c38
@@ -0,0 +1,2 @@
+[trace2]
+	eventNesting = 3
contrib/libgit-sys/src/lib.rs +4 -0
index dadb4e5f40..4bfc650450 100644
@@ -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;
 
t/Makefile +7 -2
index 53ba01c21b..2994eb5fa9 100644
@@ -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
Signature · 1 line
2.48.1.362.g079036d154-goog
#215 lore raw

Re: [PATCH v9 0/4] Introduce libgit-rs, a Rust wrapper around libgit.a

In reply to Josh Steadmon

Hi Josh

The range-diff looks good. It's really nice to see this being added to git.

Best Wishes

Phillip

Quoted 209 lines · Josh Steadmon

This series provides two small Rust wrapper libraries around parts of
Git: "libgit-sys", which exposes a few functions from libgit.a, and "libgit", which provides a more Rust-friendly interface to some of those functions. In addition to included unit tests, at $DAYJOB we have tested building JJ[1] with our library and used it to replace some of the libgit2-rs uses.

[1] https://github.com/jj-vcs/jj

There is known NEEDSWORK, but I feel that they can be addressed in follow-up changes, rather than in this series. If you feel otherwise, please let me know:

* Investigate alternative methods of managing symbol visibility & renaming.

* Figure out symbol versioning

Changes in V9:
* Properly initialize Makefile var LIBGIT_PUB_OBJS.

* Pass the correct pointer to free in libgit_configset_free().

Changes in V8:
* Define a private libgit_config_set struct to avoid excessive casting in public_symbol_export.c.

* Style fixes: merge some Makefile rules, limit rule line length by defining intermediate variables, add initial empty comment line, add linebreaks in function definitions.

Changes in V7:
* Moved the ConfigSet implementation in libgit-rs to a config module.

* Added doc comments for ConfigSet and its methods.

* Fix meson builds by adding new object files to libgit_sources

* Moved INCLUDE_LIBGIT_RS Makefile changes earlier in the series, so that we can make it optional to compile some of the libgitpub sources. Squashed V6 patch 5/5 into this series' patch 4/4.

* Don't publicly export FFI types in libgit-rs.

* Removed extraneous -r argument to $(RM) in the clean rules.

* Added TODO reminder in Cargo.toml about removing Cargo.lock once we hit a certain minimum supported Rust version.

* Style cleanup in public_symbol_export.c

Changes in V6:
* Rebased onto current master, since V5 was several months old.

* Move libgit-sys out of libgit-rs; while this sort of nesting is common in Rust crates with standalone repositories, it doesn't make as much sense when they're contained in the larger Git project's repo.

* Standardize the naming of some of the Makefile targets to always include a dash in the "-rs" or "-sys" suffixes.

* Clean up READMEs and crate descriptions in preparation for uploading to crates.io.

Changes in V5:
* When building with INCLUDE_LIBGIT_RS defined, add "-fvisibility=hidden" to CFLAGS. This allows us to manage symbol visibility in libgitpub.a without causing make all to rebuild from scratch due to changing CFLAGS.

* Avoid using c_int in the higher-level Rust API.

* Remove libgitpub.a and intermediate files with make clean.

Changes in V4:
* Drop V3 patch #3, which added wrappers around repository initialization and config access. These are not well-libified, and they are not necessary for JJ's proof-of-concept use case, so let's avoid exporting them for now.

* Set a minimum supported Rust version of 1.63. Autodetect whether our Rust version has c_int and c_char types; if not, define them ourselves.

* When building libgitpub.a via build.rs, set DEVELOPER=1 to catch additional errors at build time.

* In build.rs, use the make_cmd crate to portable select the correct invocation of GNU Make.

* Follow naming standards for _alloc() and _free() functions.

* Use String instead of CString in higher-level API.

* Move libgit_configset_alloc() and libgit_configset_free() out of upstream Git, to the libgitpub shim library.

* In libgitpub, initialize libgit_config_set structs in the _alloc() function rather than with a separate _init() function.

* Remove unnecessary comments in libgit-sys showing where the wrapped functions were originally defined.

* Fix clippy lint: don't reborrow configfile path references.

* Various typo fixes and cargo fmt fixes.

Changes in V3:
* Renamed cgit-rs to libgit-rs and cgit-sys to libgit-sys

* Makefile cleanup, particularly adding config.mak options that developers can set to run Rust builds and tests by default (Patch 6)

* Provide testdata configs for unit tests

* ConfigSet API now uses &Path instead of &str -- more ergonomic for Rust users to pass in and errors out if the path string isn't UTF-8

* Fixed unresolved dependency on libz in Cargo.toml

Calvin Wan (1):
libgit: add higher-level libgit crate

Josh Steadmon (3):
common-main: split init and exit code into new files
libgit-sys: introduce Rust wrapper for libgit.a
libgit-sys: also export some config_set functions

  .gitignore                                |   2 +
  Makefile                                  |  50 ++++++++++
  common-exit.c                             |  26 ++++++
  common-init.c                             |  63 +++++++++++++
  common-init.h                             |   6 ++
  common-main.c                             |  83 +----------------
  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/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 |  59 ++++++++++++
  contrib/libgit-sys/public_symbol_export.h |  18 ++++
  contrib/libgit-sys/src/lib.rs             |  79 ++++++++++++++++
  meson.build                               |   2 +
  t/Makefile                                |  15 +++
  24 files changed, 673 insertions(+), 81 deletions(-)

create mode 100644 common-exit.c
create mode 100644 common-init.c
create mode 100644 common-init.h
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 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

Range-diff against v8:

1:  cd0cb9aa04 = 1:  cd0cb9aa04 common-main: split init and exit code into new files
2:  3588a3c3fc ! 2:  8793ff64a7 libgit-sys: introduce Rust wrapper for libgit.a
     @@ Makefile: $(CLAR_TEST_PROG): $(UNIT_TEST_DIR)/clar.suite $(CLAR_TEST_OBJS) $(GIT
      +all:: libgit-sys
      +endif
      +
     -+LIBGIT_PUB_OBJS = contrib/libgit-sys/public_symbol_export.o
     ++LIBGIT_PUB_OBJS =
     ++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
3:  f4452fffe6 ! 3:  ab32bd1d07 libgit-sys: also export some config_set functions
     @@ contrib/libgit-sys/public_symbol_export.c
      +void libgit_configset_free(struct libgit_config_set *cs)
      +{
      +	git_configset_clear(&cs->cs);
     -+	free(&cs->cs);
     ++	free(cs);
      +}
      +
      +int libgit_configset_add_file(struct libgit_config_set *cs, const char *filename)

4: ada9fc0a13 ! 4: 1bf8c5392c libgit: add higher-level libgit crate
@@ Makefile: build-unit-tests: $(UNIT_TEST_PROGS) $(CLAR_TEST_PROG)
+all:: libgit-sys libgit-rs
endif

- LIBGIT_PUB_OBJS = contrib/libgit-sys/public_symbol_export.o
+ LIBGIT_PUB_OBJS =

## contrib/libgit-rs/Cargo.lock (new) ##
@@

base-commit: 757161efcca150a9a96b312d9e780a071e601a03

#216 lore raw

Re: [PATCH v9 0/4] Introduce libgit-rs, a Rust wrapper around libgit.a

Quoted 7 lines · [email protected] writes:

Hi Josh

The range-diff looks good. It's really nice to see this being added to git.

Best Wishes

Phillip

Thanks. Let's plan to mark the topic for 'next' unless there are other comments.