Merge branch 'js/libgit-rust'
Foreign language interface for Rust into our code base has been added. * js/libgit-rust: libgit: add higher-level libgit crate libgit-sys: also export some config_set functions libgit-sys: introduce Rust wrapper for libgit.a common-main: split init and exit code into new files
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -250,3 +250,5 @@ Release/
|
|||||||
/git.VC.db
|
/git.VC.db
|
||||||
*.dSYM
|
*.dSYM
|
||||||
/contrib/buildsystems/out
|
/contrib/buildsystems/out
|
||||||
|
/contrib/libgit-rs/target
|
||||||
|
/contrib/libgit-sys/target
|
||||||
|
|||||||
50
Makefile
50
Makefile
@@ -417,6 +417,9 @@ include shared.mak
|
|||||||
# Define LINK_FUZZ_PROGRAMS if you want `make all` to also build the fuzz test
|
# Define LINK_FUZZ_PROGRAMS if you want `make all` to also build the fuzz test
|
||||||
# programs in oss-fuzz/.
|
# 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-sys and contrib/libgit-rs.
|
||||||
|
#
|
||||||
# === Optional library: libintl ===
|
# === Optional library: libintl ===
|
||||||
#
|
#
|
||||||
# Define NO_GETTEXT if you don't want Git output to be translated.
|
# Define NO_GETTEXT if you don't want Git output to be translated.
|
||||||
@@ -658,6 +661,8 @@ CURL_CONFIG = curl-config
|
|||||||
GCOV = gcov
|
GCOV = gcov
|
||||||
STRIP = strip
|
STRIP = strip
|
||||||
SPATCH = spatch
|
SPATCH = spatch
|
||||||
|
LD = ld
|
||||||
|
OBJCOPY = objcopy
|
||||||
|
|
||||||
export TCL_PATH TCLTK_PATH
|
export TCL_PATH TCLTK_PATH
|
||||||
|
|
||||||
@@ -676,6 +681,7 @@ FUZZ_OBJS =
|
|||||||
FUZZ_PROGRAMS =
|
FUZZ_PROGRAMS =
|
||||||
GIT_OBJS =
|
GIT_OBJS =
|
||||||
LIB_OBJS =
|
LIB_OBJS =
|
||||||
|
LIBGIT_PUB_OBJS =
|
||||||
SCALAR_OBJS =
|
SCALAR_OBJS =
|
||||||
OBJECTS =
|
OBJECTS =
|
||||||
OTHER_PROGRAMS =
|
OTHER_PROGRAMS =
|
||||||
@@ -984,6 +990,8 @@ LIB_OBJS += combine-diff.o
|
|||||||
LIB_OBJS += commit-graph.o
|
LIB_OBJS += commit-graph.o
|
||||||
LIB_OBJS += commit-reach.o
|
LIB_OBJS += commit-reach.o
|
||||||
LIB_OBJS += commit.o
|
LIB_OBJS += commit.o
|
||||||
|
LIB_OBJS += common-exit.o
|
||||||
|
LIB_OBJS += common-init.o
|
||||||
LIB_OBJS += compat/nonblock.o
|
LIB_OBJS += compat/nonblock.o
|
||||||
LIB_OBJS += compat/obstack.o
|
LIB_OBJS += compat/obstack.o
|
||||||
LIB_OBJS += compat/terminal.o
|
LIB_OBJS += compat/terminal.o
|
||||||
@@ -2252,6 +2260,12 @@ ifdef WITH_BREAKING_CHANGES
|
|||||||
BASIC_CFLAGS += -DWITH_BREAKING_CHANGES
|
BASIC_CFLAGS += -DWITH_BREAKING_CHANGES
|
||||||
endif
|
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),)
|
ifeq ($(TCLTK_PATH),)
|
||||||
NO_TCLTK = NoThanks
|
NO_TCLTK = NoThanks
|
||||||
endif
|
endif
|
||||||
@@ -2748,6 +2762,10 @@ OBJECTS += $(UNIT_TEST_OBJS)
|
|||||||
OBJECTS += $(CLAR_TEST_OBJS)
|
OBJECTS += $(CLAR_TEST_OBJS)
|
||||||
OBJECTS += $(patsubst %,$(UNIT_TEST_DIR)/%.o,$(UNIT_TEST_PROGRAMS))
|
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
|
ifndef NO_CURL
|
||||||
OBJECTS += http.o http-walker.o remote-curl.o
|
OBJECTS += http.o http-walker.o remote-curl.o
|
||||||
endif
|
endif
|
||||||
@@ -3743,6 +3761,10 @@ clean: profile-clean coverage-clean cocciclean
|
|||||||
$(RM) $(htmldocs).tar.gz $(manpages).tar.gz
|
$(RM) $(htmldocs).tar.gz $(manpages).tar.gz
|
||||||
$(MAKE) -C Documentation/ clean
|
$(MAKE) -C Documentation/ clean
|
||||||
$(RM) Documentation/GIT-EXCLUDED-PROGRAMS
|
$(RM) Documentation/GIT-EXCLUDED-PROGRAMS
|
||||||
|
$(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
|
||||||
ifndef NO_PERL
|
ifndef NO_PERL
|
||||||
$(RM) -r perl/build/
|
$(RM) -r perl/build/
|
||||||
endif
|
endif
|
||||||
@@ -3904,3 +3926,31 @@ $(CLAR_TEST_PROG): $(UNIT_TEST_DIR)/clar.suite $(CLAR_TEST_OBJS) $(GITLIBS) GIT-
|
|||||||
build-unit-tests: $(UNIT_TEST_PROGS) $(CLAR_TEST_PROG)
|
build-unit-tests: $(UNIT_TEST_PROGS) $(CLAR_TEST_PROG)
|
||||||
unit-tests: $(UNIT_TEST_PROGS) $(CLAR_TEST_PROG) t/helper/test-tool$X
|
unit-tests: $(UNIT_TEST_PROGS) $(CLAR_TEST_PROG) t/helper/test-tool$X
|
||||||
$(MAKE) -C t/ unit-tests
|
$(MAKE) -C t/ unit-tests
|
||||||
|
|
||||||
|
.PHONY: libgit-sys libgit-rs
|
||||||
|
libgit-sys libgit-rs:
|
||||||
|
$(QUIET)(\
|
||||||
|
cd contrib/$@ && \
|
||||||
|
cargo build \
|
||||||
|
)
|
||||||
|
ifdef INCLUDE_LIBGIT_RS
|
||||||
|
all:: libgit-sys libgit-rs
|
||||||
|
endif
|
||||||
|
|
||||||
|
LIBGIT_PUB_OBJS += contrib/libgit-sys/public_symbol_export.o
|
||||||
|
LIBGIT_PUB_OBJS += libgit.a
|
||||||
|
LIBGIT_PUB_OBJS += reftable/libreftable.a
|
||||||
|
LIBGIT_PUB_OBJS += xdiff/lib.a
|
||||||
|
|
||||||
|
LIBGIT_PARTIAL_EXPORT = contrib/libgit-sys/partial_symbol_export.o
|
||||||
|
|
||||||
|
LIBGIT_HIDDEN_EXPORT = contrib/libgit-sys/hidden_symbol_export.o
|
||||||
|
|
||||||
|
$(LIBGIT_PARTIAL_EXPORT): $(LIBGIT_PUB_OBJS)
|
||||||
|
$(LD) -r $^ -o $@
|
||||||
|
|
||||||
|
$(LIBGIT_HIDDEN_EXPORT): $(LIBGIT_PARTIAL_EXPORT)
|
||||||
|
$(OBJCOPY) --localize-hidden $^ $@
|
||||||
|
|
||||||
|
contrib/libgit-sys/libgitpub.a: $(LIBGIT_HIDDEN_EXPORT)
|
||||||
|
$(AR) $(ARFLAGS) $@ $^
|
||||||
|
|||||||
26
common-exit.c
Normal file
26
common-exit.c
Normal file
@@ -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;
|
||||||
|
}
|
||||||
63
common-init.c
Normal file
63
common-init.c
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
#define USE_THE_REPOSITORY_VARIABLE
|
||||||
|
|
||||||
|
#include "git-compat-util.h"
|
||||||
|
#include "common-init.h"
|
||||||
|
#include "exec-cmd.h"
|
||||||
|
#include "gettext.h"
|
||||||
|
#include "attr.h"
|
||||||
|
#include "repository.h"
|
||||||
|
#include "setup.h"
|
||||||
|
#include "strbuf.h"
|
||||||
|
#include "trace2.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Many parts of Git have subprograms communicate via pipe, expect the
|
||||||
|
* upstream of a pipe to die with SIGPIPE when the downstream of a
|
||||||
|
* pipe does not need to read all that is written. Some third-party
|
||||||
|
* programs that ignore or block SIGPIPE for their own reason forget
|
||||||
|
* to restore SIGPIPE handling to the default before spawning Git and
|
||||||
|
* break this carefully orchestrated machinery.
|
||||||
|
*
|
||||||
|
* Restore the way SIGPIPE is handled to default, which is what we
|
||||||
|
* expect.
|
||||||
|
*/
|
||||||
|
static void restore_sigpipe_to_default(void)
|
||||||
|
{
|
||||||
|
sigset_t unblock;
|
||||||
|
|
||||||
|
sigemptyset(&unblock);
|
||||||
|
sigaddset(&unblock, SIGPIPE);
|
||||||
|
sigprocmask(SIG_UNBLOCK, &unblock, NULL);
|
||||||
|
signal(SIGPIPE, SIG_DFL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void init_git(const char **argv)
|
||||||
|
{
|
||||||
|
struct strbuf tmp = STRBUF_INIT;
|
||||||
|
|
||||||
|
trace2_initialize_clock();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Always open file descriptors 0/1/2 to avoid clobbering files
|
||||||
|
* in die(). It also avoids messing up when the pipes are dup'ed
|
||||||
|
* onto stdin/stdout/stderr in the child processes we spawn.
|
||||||
|
*/
|
||||||
|
sanitize_stdfds();
|
||||||
|
restore_sigpipe_to_default();
|
||||||
|
|
||||||
|
git_resolve_executable_dir(argv[0]);
|
||||||
|
|
||||||
|
setlocale(LC_CTYPE, "");
|
||||||
|
git_setup_gettext();
|
||||||
|
|
||||||
|
initialize_repository(the_repository);
|
||||||
|
|
||||||
|
attr_start();
|
||||||
|
|
||||||
|
trace2_initialize();
|
||||||
|
trace2_cmd_start(argv);
|
||||||
|
trace2_collect_process_info(TRACE2_PROCESS_INFO_STARTUP);
|
||||||
|
|
||||||
|
if (!strbuf_getcwd(&tmp))
|
||||||
|
tmp_original_cwd = strbuf_detach(&tmp, NULL);
|
||||||
|
}
|
||||||
6
common-init.h
Normal file
6
common-init.h
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
#ifndef COMMON_INIT_H
|
||||||
|
#define COMMON_INIT_H
|
||||||
|
|
||||||
|
void init_git(const char **argv);
|
||||||
|
|
||||||
|
#endif /* COMMON_INIT_H */
|
||||||
@@ -1,92 +1,13 @@
|
|||||||
#define USE_THE_REPOSITORY_VARIABLE
|
|
||||||
|
|
||||||
#include "git-compat-util.h"
|
#include "git-compat-util.h"
|
||||||
#include "exec-cmd.h"
|
#include "common-init.h"
|
||||||
#include "gettext.h"
|
|
||||||
#include "attr.h"
|
|
||||||
#include "repository.h"
|
|
||||||
#include "setup.h"
|
|
||||||
#include "strbuf.h"
|
|
||||||
#include "trace2.h"
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Many parts of Git have subprograms communicate via pipe, expect the
|
|
||||||
* upstream of a pipe to die with SIGPIPE when the downstream of a
|
|
||||||
* pipe does not need to read all that is written. Some third-party
|
|
||||||
* programs that ignore or block SIGPIPE for their own reason forget
|
|
||||||
* to restore SIGPIPE handling to the default before spawning Git and
|
|
||||||
* break this carefully orchestrated machinery.
|
|
||||||
*
|
|
||||||
* Restore the way SIGPIPE is handled to default, which is what we
|
|
||||||
* expect.
|
|
||||||
*/
|
|
||||||
static void restore_sigpipe_to_default(void)
|
|
||||||
{
|
|
||||||
sigset_t unblock;
|
|
||||||
|
|
||||||
sigemptyset(&unblock);
|
|
||||||
sigaddset(&unblock, SIGPIPE);
|
|
||||||
sigprocmask(SIG_UNBLOCK, &unblock, NULL);
|
|
||||||
signal(SIGPIPE, SIG_DFL);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, const char **argv)
|
int main(int argc, const char **argv)
|
||||||
{
|
{
|
||||||
int result;
|
int result;
|
||||||
struct strbuf tmp = STRBUF_INIT;
|
|
||||||
|
|
||||||
trace2_initialize_clock();
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Always open file descriptors 0/1/2 to avoid clobbering files
|
|
||||||
* in die(). It also avoids messing up when the pipes are dup'ed
|
|
||||||
* onto stdin/stdout/stderr in the child processes we spawn.
|
|
||||||
*/
|
|
||||||
sanitize_stdfds();
|
|
||||||
restore_sigpipe_to_default();
|
|
||||||
|
|
||||||
git_resolve_executable_dir(argv[0]);
|
|
||||||
|
|
||||||
setlocale(LC_CTYPE, "");
|
|
||||||
git_setup_gettext();
|
|
||||||
|
|
||||||
initialize_repository(the_repository);
|
|
||||||
|
|
||||||
attr_start();
|
|
||||||
|
|
||||||
trace2_initialize();
|
|
||||||
trace2_cmd_start(argv);
|
|
||||||
trace2_collect_process_info(TRACE2_PROCESS_INFO_STARTUP);
|
|
||||||
|
|
||||||
if (!strbuf_getcwd(&tmp))
|
|
||||||
tmp_original_cwd = strbuf_detach(&tmp, NULL);
|
|
||||||
|
|
||||||
|
init_git(argv);
|
||||||
result = cmd_main(argc, argv);
|
result = cmd_main(argc, argv);
|
||||||
|
|
||||||
/* Not exit(3), but a wrapper calling our common_exit() */
|
/* Not exit(3), but a wrapper calling our common_exit() */
|
||||||
exit(result);
|
exit(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void check_bug_if_BUG(void)
|
|
||||||
{
|
|
||||||
if (!bug_called_must_BUG)
|
|
||||||
return;
|
|
||||||
BUG("on exit(): had bug() call(s) in this process without explicit BUG_if_bug()");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* We wrap exit() to call common_exit() in git-compat-util.h */
|
|
||||||
int common_exit(const char *file, int line, int code)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* For non-POSIX systems: Take the lowest 8 bits of the "code"
|
|
||||||
* to e.g. turn -1 into 255. On a POSIX system this is
|
|
||||||
* redundant, see exit(3) and wait(2), but as it doesn't harm
|
|
||||||
* anything there we don't need to guard this with an "ifdef".
|
|
||||||
*/
|
|
||||||
code &= 0xff;
|
|
||||||
|
|
||||||
check_bug_if_BUG();
|
|
||||||
trace2_cmd_exit_fl(file, line, code);
|
|
||||||
|
|
||||||
return code;
|
|
||||||
}
|
|
||||||
|
|||||||
77
contrib/libgit-rs/Cargo.lock
generated
Normal file
77
contrib/libgit-rs/Cargo.lock
generated
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "autocfg"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cc"
|
||||||
|
version = "1.1.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "57b6a275aa2903740dc87da01c62040406b8812552e97129a63ea8850a17c6e6"
|
||||||
|
dependencies = [
|
||||||
|
"shlex",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.158"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libgit"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"libgit-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libgit-sys"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"libz-sys",
|
||||||
|
"make-cmd",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libz-sys"
|
||||||
|
version = "1.1.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d2d16453e800a8cf6dd2fc3eb4bc99b786a9b90c663b8559a5b1a041bf89e472"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"libc",
|
||||||
|
"pkg-config",
|
||||||
|
"vcpkg",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "make-cmd"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a8ca8afbe8af1785e09636acb5a41e08a765f5f0340568716c18a8700ba3c0d3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pkg-config"
|
||||||
|
version = "0.3.30"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "shlex"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "vcpkg"
|
||||||
|
version = "0.2.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||||
17
contrib/libgit-rs/Cargo.toml
Normal file
17
contrib/libgit-rs/Cargo.toml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
[package]
|
||||||
|
name = "libgit"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
build = "build.rs"
|
||||||
|
rust-version = "1.63" # TODO: Once we hit 1.84 or newer, we may want to remove Cargo.lock from
|
||||||
|
# version control. See https://lore.kernel.org/git/Z47jgK-oMjFRSslr@tapette.crustytoothpaste.net/
|
||||||
|
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
path = "src/lib.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
libgit-sys = { version = "0.1.0", path = "../libgit-sys" }
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
autocfg = "1.4.0"
|
||||||
13
contrib/libgit-rs/README.md
Normal file
13
contrib/libgit-rs/README.md
Normal file
@@ -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).
|
||||||
4
contrib/libgit-rs/build.rs
Normal file
4
contrib/libgit-rs/build.rs
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
pub fn main() {
|
||||||
|
let ac = autocfg::new();
|
||||||
|
ac.emit_has_path("std::ffi::c_char");
|
||||||
|
}
|
||||||
106
contrib/libgit-rs/src/config.rs
Normal file
106
contrib/libgit-rs/src/config.rs
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
use std::ffi::{c_void, CStr, CString};
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
#[cfg(has_std__ffi__c_char)]
|
||||||
|
use std::ffi::{c_char, c_int};
|
||||||
|
|
||||||
|
#[cfg(not(has_std__ffi__c_char))]
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
type c_char = i8;
|
||||||
|
|
||||||
|
#[cfg(not(has_std__ffi__c_char))]
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
type c_int = i32;
|
||||||
|
|
||||||
|
use libgit_sys::*;
|
||||||
|
|
||||||
|
/// A ConfigSet is an in-memory cache for config-like files such as `.gitmodules` or `.gitconfig`.
|
||||||
|
/// It does not support all config directives; notably, it will not process `include` or
|
||||||
|
/// `includeIf` directives (but it will store them so that callers can choose whether and how to
|
||||||
|
/// handle them).
|
||||||
|
pub struct ConfigSet(*mut libgit_config_set);
|
||||||
|
impl ConfigSet {
|
||||||
|
/// Allocate a new ConfigSet
|
||||||
|
pub fn new() -> Self {
|
||||||
|
unsafe { ConfigSet(libgit_configset_alloc()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Load the given files into the ConfigSet; conflicting directives in later files will
|
||||||
|
/// override those given in earlier files.
|
||||||
|
pub fn add_files(&mut self, files: &[&Path]) {
|
||||||
|
for file in files {
|
||||||
|
let pstr = file.to_str().expect("Invalid UTF-8");
|
||||||
|
let rs = CString::new(pstr).expect("Couldn't convert to CString");
|
||||||
|
unsafe {
|
||||||
|
libgit_configset_add_file(self.0, rs.as_ptr());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Load the value for the given key and attempt to parse it as an i32. Dies with a fatal error
|
||||||
|
/// if the value cannot be parsed. Returns None if the key is not present.
|
||||||
|
pub fn get_int(&mut self, key: &str) -> Option<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())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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();
|
||||||
|
unsafe {
|
||||||
|
if libgit_configset_get_string(self.0, key.as_ptr(), &mut val as *mut *mut c_char) != 0
|
||||||
|
{
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let borrowed_str = CStr::from_ptr(val);
|
||||||
|
let owned_str =
|
||||||
|
String::from(borrowed_str.to_str().expect("Couldn't convert val to str"));
|
||||||
|
free(val as *mut c_void); // Free the xstrdup()ed pointer from the C side
|
||||||
|
Some(owned_str)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ConfigSet {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for ConfigSet {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
libgit_configset_free(self.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn load_configs_via_configset() {
|
||||||
|
let mut cs = ConfigSet::new();
|
||||||
|
cs.add_files(&[
|
||||||
|
Path::new("testdata/config1"),
|
||||||
|
Path::new("testdata/config2"),
|
||||||
|
Path::new("testdata/config3"),
|
||||||
|
]);
|
||||||
|
// ConfigSet retrieves correct value
|
||||||
|
assert_eq!(cs.get_int("trace2.eventTarget"), Some(1));
|
||||||
|
// ConfigSet respects last config value set
|
||||||
|
assert_eq!(cs.get_int("trace2.eventNesting"), Some(3));
|
||||||
|
// ConfigSet returns None for missing key
|
||||||
|
assert_eq!(cs.get_string("foo.bar"), None);
|
||||||
|
}
|
||||||
|
}
|
||||||
1
contrib/libgit-rs/src/lib.rs
Normal file
1
contrib/libgit-rs/src/lib.rs
Normal file
@@ -0,0 +1 @@
|
|||||||
|
pub mod config;
|
||||||
2
contrib/libgit-rs/testdata/config1
vendored
Normal file
2
contrib/libgit-rs/testdata/config1
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
[trace2]
|
||||||
|
eventNesting = 1
|
||||||
2
contrib/libgit-rs/testdata/config2
vendored
Normal file
2
contrib/libgit-rs/testdata/config2
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
[trace2]
|
||||||
|
eventTarget = 1
|
||||||
2
contrib/libgit-rs/testdata/config3
vendored
Normal file
2
contrib/libgit-rs/testdata/config3
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
[trace2]
|
||||||
|
eventNesting = 3
|
||||||
69
contrib/libgit-sys/Cargo.lock
generated
Normal file
69
contrib/libgit-sys/Cargo.lock
generated
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "autocfg"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cc"
|
||||||
|
version = "1.1.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "57b6a275aa2903740dc87da01c62040406b8812552e97129a63ea8850a17c6e6"
|
||||||
|
dependencies = [
|
||||||
|
"shlex",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.158"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libgit-sys"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"libz-sys",
|
||||||
|
"make-cmd",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libz-sys"
|
||||||
|
version = "1.1.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d2d16453e800a8cf6dd2fc3eb4bc99b786a9b90c663b8559a5b1a041bf89e472"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"libc",
|
||||||
|
"pkg-config",
|
||||||
|
"vcpkg",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "make-cmd"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a8ca8afbe8af1785e09636acb5a41e08a765f5f0340568716c18a8700ba3c0d3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pkg-config"
|
||||||
|
version = "0.3.30"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "shlex"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "vcpkg"
|
||||||
|
version = "0.2.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||||
19
contrib/libgit-sys/Cargo.toml
Normal file
19
contrib/libgit-sys/Cargo.toml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
[package]
|
||||||
|
name = "libgit-sys"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
build = "build.rs"
|
||||||
|
links = "gitpub"
|
||||||
|
rust-version = "1.63" # TODO: Once we hit 1.84 or newer, we may want to remove Cargo.lock from
|
||||||
|
# version control. See https://lore.kernel.org/git/Z47jgK-oMjFRSslr@tapette.crustytoothpaste.net/
|
||||||
|
description = "Native bindings to a portion of libgit"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
path = "src/lib.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
libz-sys = "1.1.19"
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
autocfg = "1.4.0"
|
||||||
|
make-cmd = "0.1.0"
|
||||||
4
contrib/libgit-sys/README.md
Normal file
4
contrib/libgit-sys/README.md
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
# libgit-sys
|
||||||
|
|
||||||
|
A small proof-of-concept crate showing how to provide a Rust FFI to Git
|
||||||
|
internals.
|
||||||
35
contrib/libgit-sys/build.rs
Normal file
35
contrib/libgit-sys/build.rs
Normal file
@@ -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(())
|
||||||
|
}
|
||||||
59
contrib/libgit-sys/public_symbol_export.c
Normal file
59
contrib/libgit-sys/public_symbol_export.c
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* 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 "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();
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *libgit_user_agent_sanitized(void)
|
||||||
|
{
|
||||||
|
return git_user_agent_sanitized();
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma GCC visibility pop
|
||||||
18
contrib/libgit-sys/public_symbol_export.h
Normal file
18
contrib/libgit-sys/public_symbol_export.h
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#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);
|
||||||
|
|
||||||
|
#endif /* PUBLIC_SYMBOL_EXPORT_H */
|
||||||
79
contrib/libgit-sys/src/lib.rs
Normal file
79
contrib/libgit-sys/src/lib.rs
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
use std::ffi::c_void;
|
||||||
|
|
||||||
|
#[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;
|
||||||
|
|
||||||
|
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 free(ptr: *mut c_void);
|
||||||
|
|
||||||
|
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)]
|
||||||
|
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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -260,6 +260,8 @@ libgit_sources = [
|
|||||||
'commit-graph.c',
|
'commit-graph.c',
|
||||||
'commit-reach.c',
|
'commit-reach.c',
|
||||||
'commit.c',
|
'commit.c',
|
||||||
|
'common-exit.c',
|
||||||
|
'common-init.c',
|
||||||
'compat/nonblock.c',
|
'compat/nonblock.c',
|
||||||
'compat/obstack.c',
|
'compat/obstack.c',
|
||||||
'compat/terminal.c',
|
'compat/terminal.c',
|
||||||
|
|||||||
15
t/Makefile
15
t/Makefile
@@ -177,3 +177,18 @@ perf:
|
|||||||
|
|
||||||
.PHONY: pre-clean $(T) aggregate-results clean valgrind perf \
|
.PHONY: pre-clean $(T) aggregate-results clean valgrind perf \
|
||||||
check-chainlint clean-chainlint test-chainlint $(UNIT_TESTS)
|
check-chainlint clean-chainlint test-chainlint $(UNIT_TESTS)
|
||||||
|
|
||||||
|
.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 libgit-rs-test
|
||||||
|
endif
|
||||||
|
|||||||
Reference in New Issue
Block a user