internal/build:support relocatable lib

This commit is contained in:
luoliwoshang
2025-08-19 20:20:31 +08:00
parent cbac24cf97
commit 1d76f515e0
3 changed files with 197 additions and 1 deletions

View File

@@ -47,6 +47,27 @@ jobs:
with: with:
name: llama2-model name: llama2-model
path: ./_demo/llama2-c/ path: ./_demo/llama2-c/
- name: Download platform-specific demo libs
run: |
if ${{ startsWith(matrix.os, 'macos') }}; then
DEMO_PKG="cargs_darwin_arm64.zip"
else
DEMO_PKG="cargs_linux_amd64.zip"
fi
mkdir -p ./_demo/cargs/libs
cd ./_demo/cargs/libs
wget https://github.com/goplus/llpkg/releases/download/cargs/v1.0.0/${DEMO_PKG}
unzip ${DEMO_PKG}
# Process pc template files - replace {{.Prefix}} with actual path
ACTUAL_PREFIX="$(pwd)"
for tmpl in lib/pkgconfig/*.pc.tmpl; do
pc_file="${tmpl%.tmpl}"
sed "s|{{.Prefix}}|${ACTUAL_PREFIX}|g" "$tmpl" > "$pc_file"
done
echo "PKG_CONFIG_PATH=${ACTUAL_PREFIX}/lib/pkgconfig:${PKG_CONFIG_PATH}" >> $GITHUB_ENV
- name: Install further optional dependencies for demos - name: Install further optional dependencies for demos
run: | run: |
py_deps=( py_deps=(
@@ -70,6 +91,18 @@ jobs:
with: with:
go-version: ${{matrix.go}} go-version: ${{matrix.go}}
- name: Test demo without RPATH (expect failure)
run: |
echo "Testing demo without RPATH (should fail)..."
export LLGO_FULL_RPATH=false
pkg-config --libs cargs
if (cd ./_demo/cargs && llgo run .); then
echo "ERROR: cargs demo should have failed without RPATH!"
exit 1
else
echo "✓ cargs demo correctly failed without RPATH"
fi
- name: Test demos - name: Test demos
run: | run: |
# TODO(lijie): force python3-embed to be linked with python-3.12-embed # TODO(lijie): force python3-embed to be linked with python-3.12-embed
@@ -80,7 +113,8 @@ jobs:
libdir=$(pkg-config --variable=libdir python-3.12-embed) libdir=$(pkg-config --variable=libdir python-3.12-embed)
echo "libdir: $libdir" echo "libdir: $libdir"
ln -s $libdir/pkgconfig/python-3.12-embed.pc $pcdir/python3-embed.pc ln -s $libdir/pkgconfig/python-3.12-embed.pc $pcdir/python3-embed.pc
export PKG_CONFIG_PATH=$pcdir export PKG_CONFIG_PATH=$pcdir:${PKG_CONFIG_PATH}
export LLGO_FULL_RPATH=true
bash .github/workflows/test_demo.sh bash .github/workflows/test_demo.sh
- name: _xtool build tests - name: _xtool build tests

144
_demo/cargs/demo.go Normal file
View File

@@ -0,0 +1,144 @@
package main
import (
"fmt"
"os"
_ "unsafe"
"github.com/goplus/lib/c"
)
const LLGoPackage string = "link: $(pkg-config --libs cargs);"
type Option struct {
Identifier c.Char
AccessLetters *c.Char
AccessName *c.Char
ValueName *c.Char
Description *c.Char
}
type OptionContext struct {
Options *Option
OptionCount c.SizeT
Argc c.Int
Argv **c.Char
Index c.Int
InnerIndex c.Int
ErrorIndex c.Int
ErrorLetter c.Char
ForcedEnd bool
Identifier c.Char
Value *c.Char
}
// llgo:type C
type Printer func(__llgo_arg_0 c.Pointer, __llgo_arg_1 *c.Char, __llgo_va_list ...interface{}) c.Int
// llgo:link (*OptionContext).OptionInit C.cag_option_init
func (recv_ *OptionContext) OptionInit(options *Option, option_count c.SizeT, argc c.Int, argv **c.Char) {
}
// llgo:link (*OptionContext).OptionFetch C.cag_option_fetch
func (recv_ *OptionContext) OptionFetch() bool {
return false
}
// llgo:link (*OptionContext).OptionGetIdentifier C.cag_option_get_identifier
func (recv_ *OptionContext) OptionGetIdentifier() c.Char {
return 0
}
// llgo:link (*OptionContext).OptionGetValue C.cag_option_get_value
func (recv_ *OptionContext) OptionGetValue() *c.Char {
return nil
}
// llgo:link (*OptionContext).OptionGetIndex C.cag_option_get_index
func (recv_ *OptionContext) OptionGetIndex() c.Int {
return 0
}
// llgo:link (*OptionContext).OptionGetErrorIndex C.cag_option_get_error_index
func (recv_ *OptionContext) OptionGetErrorIndex() c.Int {
return 0
}
// llgo:link (*OptionContext).OptionGetErrorLetter C.cag_option_get_error_letter
func (recv_ *OptionContext) OptionGetErrorLetter() c.Char {
return 0
}
// llgo:link (*OptionContext).OptionPrintError C.cag_option_print_error
func (recv_ *OptionContext) OptionPrintError(destination *c.FILE) {
}
// llgo:link (*OptionContext).OptionPrinterError C.cag_option_printer_error
func (recv_ *OptionContext) OptionPrinterError(printer Printer, printer_ctx c.Pointer) {
}
// llgo:link (*Option).OptionPrint C.cag_option_print
func (recv_ *Option) OptionPrint(option_count c.SizeT, destination *c.FILE) {
}
// llgo:link (*Option).OptionPrinter C.cag_option_printer
func (recv_ *Option) OptionPrinter(option_count c.SizeT, printer Printer, printer_ctx c.Pointer) {
}
// llgo:link (*OptionContext).OptionPrepare C.cag_option_prepare
func (recv_ *OptionContext) OptionPrepare(options *Option, option_count c.SizeT, argc c.Int, argv **c.Char) {
}
// llgo:link (*OptionContext).OptionGet C.cag_option_get
func (recv_ *OptionContext) OptionGet() c.Char {
return 0
}
func main() {
options := []Option{
{
Identifier: 'h',
AccessLetters: c.Str("h"),
AccessName: c.Str("help"),
ValueName: nil,
Description: c.Str("Show help information"),
},
{
Identifier: 'v',
AccessLetters: c.Str("v"),
AccessName: c.Str("version"),
ValueName: nil,
Description: c.Str("Show version information"),
},
}
args := os.Args
// Convert Go string array to C-style argv
argv := make([]*int8, len(args))
for i, arg := range args {
argv[i] = c.AllocaCStr(arg)
}
// Initialize option context
var context OptionContext
context.OptionInit(&options[0], uintptr(len(options)), c.Int(len(args)), &argv[0])
// Process all options
identifierFound := false
for context.OptionFetch() {
identifierFound = true
identifier := context.OptionGetIdentifier()
switch identifier {
case 'h':
fmt.Println("Help: This is a simple command-line parser demo")
case 'v':
fmt.Println("Version: 1.0.0")
}
}
// Default output if no identifier is found
if !identifierFound {
fmt.Println("Demo Command-line Tool\nIdentifier:\n\t-h: Help\n\t-v: Version")
}
}

View File

@@ -606,6 +606,19 @@ func linkMainPkg(ctx *context, pkg *packages.Package, pkgs []*aPackage, global l
objFiles = append(objFiles, export) objFiles = append(objFiles, export)
} }
if IsFullRpathEnabled() {
exargs := make([]string, 0, ctx.nLibdir<<1)
// Treat every link-time library search path, specified by the -L parameter, as a runtime search path as well.
// This is to ensure the final executable can locate libraries with a relocatable install_name
// (e.g., "@rpath/libfoo.dylib") at runtime.
for _, arg := range linkArgs {
if strings.HasPrefix(arg, "-L") {
exargs = append(exargs, "-rpath", arg[2:])
}
}
linkArgs = append(linkArgs, exargs...)
}
err = linkObjFiles(ctx, app, objFiles, linkArgs, verbose) err = linkObjFiles(ctx, app, objFiles, linkArgs, verbose)
check(err) check(err)
@@ -1002,6 +1015,7 @@ const llgoWasmRuntime = "LLGO_WASM_RUNTIME"
const llgoWasiThreads = "LLGO_WASI_THREADS" const llgoWasiThreads = "LLGO_WASI_THREADS"
const llgoStdioNobuf = "LLGO_STDIO_NOBUF" const llgoStdioNobuf = "LLGO_STDIO_NOBUF"
const llgoCheckLinkArgs = "LLGO_CHECK_LINKARGS" const llgoCheckLinkArgs = "LLGO_CHECK_LINKARGS"
const llgoFullRpath = "LLGO_FULL_RPATH"
const defaultWasmRuntime = "wasmtime" const defaultWasmRuntime = "wasmtime"
@@ -1053,6 +1067,10 @@ func IsCheckLinkArgsEnabled() bool {
return isEnvOn(llgoCheckLinkArgs, false) return isEnvOn(llgoCheckLinkArgs, false)
} }
func IsFullRpathEnabled() bool {
return isEnvOn(llgoFullRpath, true)
}
func WasmRuntime() string { func WasmRuntime() string {
return defaultEnv(llgoWasmRuntime, defaultWasmRuntime) return defaultEnv(llgoWasmRuntime, defaultWasmRuntime)
} }