diff --git a/.gitignore b/.gitignore index a6049d3..b1808b3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ # Binary executables zsvo +zsvo-test pkg-manager # Build artifacts diff --git a/cmd/install.go b/cmd/install.go index 8fc625d..f48a896 100644 --- a/cmd/install.go +++ b/cmd/install.go @@ -191,6 +191,7 @@ type autoBuildSession struct { toolRoot string autoBuildDeps bool resolver *debian.Resolver + depResolver *debian.DependencyResolver builder *builder.Builder toolInstaller *installer.Installer builtPackages map[string]string @@ -207,6 +208,7 @@ func newAutoBuildSession(workDir string, autoBuildDeps bool) *autoBuildSession { toolRoot: filepath.Join(workDir, "bootstrap-root"), autoBuildDeps: autoBuildDeps, resolver: debian.NewResolver(), + depResolver: debian.NewDependencyResolver(), builder: b, toolInstaller: installer.NewInstaller(filepath.Join(workDir, "bootstrap-root")), builtPackages: make(map[string]string), @@ -302,14 +304,66 @@ func (s *autoBuildSession) buildPackageWithFallback(requestName string, asBuildD fmt.Printf("Building %s from %s...\n", rcp.GetPackageName(), srcInfo.DSCURL) - // ВАЖНО: Мы НЕ собираем Debian зависимости! - // Мы используем Debian только как источник исходников - // Все зависимости должны быть уже установлены в LFS системе - if len(srcInfo.BuildDepends) > 0 { - fmt.Printf("⚠️ Found Debian Build-Depends: %s\n", strings.Join(srcInfo.BuildDepends, ", ")) - fmt.Printf("ℹ️ In LFS, make sure these dependencies are available in your toolchain\n") + // Авто-разрешение зависимостей: строим все Build-Depends рекурсивно + if len(srcInfo.BuildDepends) > 0 && s.autoBuildDeps { + fmt.Printf("🔍 Resolving %d build dependencies for %s...\n", len(srcInfo.BuildDepends), requestName) + + // Collect dependencies that need to be built + depsToBuild := make([]string, 0) + for _, dep := range srcInfo.BuildDepends { + depName := extractPackageNameFromConstraint(dep) + if depName == "" { + continue + } + + // Check if this is a Debian-specific package that should be skipped + sourcePkg := mapDebianPackageToSource(depName) + if sourcePkg == "" { + // Try comprehensive resolver + sourcePkg, _ = s.depResolver.BinaryToSource(depName) + if sourcePkg == "" { + fmt.Printf(" ⚠️ Skipping Debian-specific dependency: %s\n", depName) + continue + } + } + + // Check if already built or available + if _, built := s.builtPackages[sourcePkg]; built { + continue + } + if toolAlreadyAvailable(sourcePkg) { + s.toolDepsReady[sourcePkg] = struct{}{} + continue + } + if _, ready := s.toolDepsReady[sourcePkg]; ready { + continue + } + + depsToBuild = append(depsToBuild, sourcePkg) + } + + // Build all dependencies first (in order) + if len(depsToBuild) > 0 { + fmt.Printf("📦 Need to build %d dependencies: %s\n", len(depsToBuild), strings.Join(depsToBuild, ", ")) + + for _, dep := range depsToBuild { + // Recursive build with dependency tracking + _, err := s.buildPackageWithFallback(dep, true, false, append(stack, requestName)) + if err != nil { + fmt.Printf(" ⚠️ Failed to build dependency %s: %v (continuing anyway)\n", dep, err) + // Don't fail immediately - the main package might still build + } + } + + // Refresh environment after building dependencies + s.refreshBuildEnv() + } else { + fmt.Printf(" ✅ All dependencies already available\n") + } } + // Now build the main package + var buildErr error for attempt := 0; attempt < 2; attempt++ { bar := newProgressUI(rcp.Name) @@ -335,16 +389,18 @@ func (s *autoBuildSession) buildPackageWithFallback(requestName string, asBuildD } builtPackage := filepath.Join(rcp.GetPackageDir(s.workDir), rcp.GetPackageFileName()) - s.builtPackages[rcp.Name] = builtPackage if buildErr != nil { if !allowFailure { return "", fmt.Errorf("failed to auto-build %s: %w", requestName, buildErr) } - if err := s.installBuildDependency(requestName, builtPackage); err != nil { - return "", err - } + // allowFailure=true means we tolerate build failure, but we shouldn't try to install a broken package + // Log and return empty - caller should handle gracefully + fmt.Printf("Warning: build of %s failed but allowFailure=true, skipping installation of broken package\n", requestName) + return "", fmt.Errorf("build failed for %s (allowFailure set): %w", requestName, buildErr) } + + s.builtPackages[rcp.Name] = builtPackage return builtPackage, nil } @@ -353,8 +409,12 @@ func (s *autoBuildSession) installSystemPackage(pkg string, currentBuildingPacka // Try to resolve and build the package as a dependency fmt.Printf("Building system dependency %s through zsvo...\n", pkg) - // Map common Debian package names to source packages - sourcePkg := mapDebianPackageToSource(pkg) + // Use comprehensive Debian resolver to map binary package to source + sourcePkg, err := s.depResolver.BinaryToSource(pkg) + if err != nil { + // Fall back to built-in mapping for common packages + sourcePkg = mapDebianPackageToSource(pkg) + } if sourcePkg == "" { fmt.Printf("Skipping Debian-specific dependency: %s\n", pkg) return nil // Skip Debian-specific packages @@ -367,7 +427,7 @@ func (s *autoBuildSession) installSystemPackage(pkg string, currentBuildingPacka } // Try to build the dependency - _, err := s.buildPackageWithFallback(sourcePkg, true, false, []string{}) + _, err = s.buildPackageWithFallback(sourcePkg, true, false, []string{}) if err != nil { return fmt.Errorf("failed to build system dependency %s (mapped from %s): %w", sourcePkg, pkg, err) } @@ -403,8 +463,12 @@ func (s *autoBuildSession) ensureBuildDependency(dep string, stack []string) err fmt.Printf("Installing build dependency %s into %s...\n", dep, s.toolRoot) packagePath, err := s.buildPackageWithFallback(dep, true, false, append(stack, dep)) if err != nil { - fmt.Printf("Warning: failed to build dependency %s: %v (continuing anyway)\n", dep, err) - // Continue anyway instead of failing + fmt.Printf("Warning: failed to build dependency %s: %v (skipping installation)\n", dep, err) + return nil // Don't try to install a package that failed to build + } + if packagePath == "" { + fmt.Printf("Warning: build succeeded but no package path returned for %s (skipping installation)\n", dep) + return nil } return s.installBuildDependency(dep, packagePath) } @@ -958,9 +1022,12 @@ func autoRecipeFromDebian(src *debian.SourceInfo) *recipe.Recipe { version = "0" } - // Use external install script to avoid shell escaping issues + // Use inline install commands instead of external script to avoid hardcoded paths installCommands := []string{ - "PKGDIR={{pkgdir}} bash /tmp/install-script.sh", + "if [ -f build/cmake_install.cmake ]; then DESTDIR={{pkgdir}} cmake --install build; " + + "elif [ -f build/meson-private/coredata.dat ]; then DESTDIR={{pkgdir}} meson install -C build; " + + "elif [ -f Makefile ] || [ -f makefile ] || [ -f GNUmakefile ]; then make DESTDIR={{pkgdir}} PREFIX=/usr install; " + + "else mkdir -p {{pkgdir}}/usr/bin {{pkgdir}}/usr/lib {{pkgdir}}/usr/include; fi", } return &recipe.Recipe{ @@ -974,8 +1041,8 @@ func autoRecipeFromDebian(src *debian.SourceInfo) *recipe.Recipe { Build: []string{ "if [ ! -f configure ] && [ -f autogen.sh ]; then sh ./autogen.sh --no-check ${ZSVO_AUTOGEN_ARGS}; fi; if [ -f configure ]; then ./configure --prefix=/usr ${ZSVO_CONFIGURE_FLAGS}; fi", "# Download missing dependencies automatically", - "if [ ! -f \"src/3rdparty/yyjson/yyjson.c\" ]; then echo \"Downloading yyjson...\" && mkdir -p src/3rdparty/yyjson && curl -L https://raw.githubusercontent.com/yyjson/yyjson.c/master/src/yyjson.c -o src/3rdparty/yyjson/yyjson.c; fi", - "if [ ! -f \"src/3rdparty/yyjson/yyjson.h\" ]; then echo \"Downloading yyjson header...\" && mkdir -p src/3rdparty/yyjson && curl -L https://raw.githubusercontent.com/yyjson/yyjson.c/master/src/yyjson.h -o src/3rdparty/yyjson/yyjson.h; fi", + "if [ ! -f \"src/3rdparty/yyjson/yyjson.c\" ]; then echo \"Downloading yyjson...\" && mkdir -p src/3rdparty/yyjson && curl -L https://raw.githubusercontent.com/ibireme/yyjson/master/src/yyjson.c -o src/3rdparty/yyjson/yyjson.c; fi", + "if [ ! -f \"src/3rdparty/yyjson/yyjson.h\" ]; then echo \"Downloading yyjson header...\" && mkdir -p src/3rdparty/yyjson && curl -L https://raw.githubusercontent.com/ibireme/yyjson/master/src/yyjson.h -o src/3rdparty/yyjson/yyjson.h; fi", "if [ -f CMakeLists.txt ]; then cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr -DUNIX=1 -DCMAKE_DISABLE_FIND_PACKAGE_Win32=1 ${ZSVO_CMAKE_FLAGS} && cmake --build build -j${jobs} -- ${ZSVO_CMAKE_BUILD_FLAGS}; " + "elif [ -f meson.build ]; then meson setup build --prefix=/usr ${ZSVO_MESON_SETUP_ARGS} && meson compile -C build -j${jobs} ${ZSVO_MESON_COMPILE_ARGS}; " + "elif [ -f Makefile ] || [ -f makefile ] || [ -f GNUmakefile ]; then make -j${jobs} ${ZSVO_MAKE_FLAGS}; fi", diff --git a/install-script.sh b/install-script.sh index e6ad5e1..0ac9d12 100644 --- a/install-script.sh +++ b/install-script.sh @@ -2,7 +2,13 @@ # Simple install script for auto-generated packages set -e -PKGDIR="$1" +# Use PKGDIR from environment, fallback to first argument for backward compatibility +PKGDIR="${PKGDIR:-$1}" + +if [ -z "$PKGDIR" ]; then + echo "Error: PKGDIR not set. Usage: PKGDIR=/path/to/dest $0" >&2 + exit 1 +fi echo "Installing to $PKGDIR" diff --git a/pkg/builder/builder.go b/pkg/builder/builder.go index a3a6590..caebd87 100644 --- a/pkg/builder/builder.go +++ b/pkg/builder/builder.go @@ -259,8 +259,8 @@ func (b *Builder) executeCommand(workDir, command string, env []string) error { cmd.Dir = workDir cmd.Env = env if b.quiet { - // Create a context with timeout to prevent hanging - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Minute) + // Create a context with timeout to prevent hanging - use 2 hours for large packages like gcc/llvm + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Hour) defer cancel() cmd := exec.CommandContext(ctx, "sh", "-c", command) @@ -277,8 +277,8 @@ func (b *Builder) executeCommand(workDir, command string, env []string) error { return nil } - // For non-quiet mode, still set a reasonable timeout - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Minute) + // For non-quiet mode, still set a reasonable timeout - use 2 hours for large packages + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Hour) defer cancel() cmd = exec.CommandContext(ctx, "sh", "-c", command) cmd.Dir = workDir @@ -614,48 +614,74 @@ func (b *Builder) validateSourceFiles(recipe *recipe.Recipe, sourceDir string) e } func (b *Builder) validateCommand(command string) error { - // List of dangerous patterns to block + // Normalize for pattern matching + cmdLower := strings.ToLower(command) + + // Remove common bypass attempts: quotes, spaces, backslashes for normalization + normalized := cmdLower + normalized = strings.ReplaceAll(normalized, `"`, "") + normalized = strings.ReplaceAll(normalized, `'`, "") + normalized = strings.ReplaceAll(normalized, ` `, "") + normalized = strings.ReplaceAll(normalized, `\`, "") + + // List of dangerous patterns to block (check both original and normalized) dangerousPatterns := []string{ "rm -rf /", "rm -rf /*", - ":(){ :|:& };:", // fork bomb + "rm -rf/", + `:(){:|:&};:`, // fork bomb (normalized) + ":(){ :|:& };:", // fork bomb (original) "chmod 777 /", "chown root", "sudo ", - "su ", + "su -", "> /dev/sda", "> /dev/hda", + ">/dev/sda", + ">/dev/hda", "mkfs", - "format", + "format /", "fdisk", - // Additional patterns "dd if=", "chmod -R 777 /", "chown -R root", - "rm -rf /*", "rm -rf /etc", "rm -rf /usr", "rm -rf /bin", "rm -rf /sbin", "rm -rf /lib", "rm -rf /lib64", + "rm-rf/etc", + "rm-rf/usr", + "rm-rf/bin", + "rm-rf/sbin", + "rm-rf/lib", + "rm-rf/lib64", "shutdown", "reboot", "halt", "poweroff", "init 0", "init 6", + "eval", + "$(", // command substitution + "${", // parameter expansion } - cmdLower := strings.ToLower(command) - - // Check for dangerous patterns + // Check against original for _, pattern := range dangerousPatterns { if strings.Contains(cmdLower, pattern) { return fmt.Errorf("command contains potentially dangerous pattern: %s", pattern) } } + // Check against normalized (catches bypass attempts like 'rm -rf "/"' or 'rm -rf / ') + for _, pattern := range dangerousPatterns { + if strings.Contains(normalized, pattern) { + return fmt.Errorf("command contains potentially dangerous pattern (normalized): %s", pattern) + } + } + // Check for suspicious characters that might indicate injection suspiciousChars := []string{"\x00", "\r", "\n", "\t"} for _, char := range suspiciousChars { @@ -676,14 +702,12 @@ func (b *Builder) validateCommand(command string) error { } // Check for command substitution patterns that could bypass security - dangerousSubstitutions := []string{ - "`", - "$|", + // Backticks and $() are blocked + if strings.Contains(command, "`") { + return fmt.Errorf("command contains potentially dangerous backtick substitution") } - for _, pattern := range dangerousSubstitutions { - if strings.Contains(command, pattern) { - return fmt.Errorf("command contains potentially dangerous substitution: %s", pattern) - } + if strings.Contains(command, "$(") { + return fmt.Errorf("command contains potentially dangerous command substitution $()") } // Log the command for audit purposes (only first 100 chars) diff --git a/pkg/debian/dep_resolver.go b/pkg/debian/dep_resolver.go new file mode 100644 index 0000000..3aed94c --- /dev/null +++ b/pkg/debian/dep_resolver.go @@ -0,0 +1,1173 @@ +package debian + +import ( + "bufio" + "fmt" + "io" + "net/http" + "regexp" + "strings" + "sync" + "time" + + "github.com/ulikunitz/xz" +) + +// DependencyResolver provides comprehensive Debian dependency resolution +type DependencyResolver struct { + resolver *Resolver + cache *dependencyCache + httpClient *http.Client + mirrors []string + suites []string + components []string + arch string +} + +// dependencyCache caches resolved dependencies +type dependencyCache struct { + mu sync.RWMutex + deps map[string]*PackageDeps + srcMap map[string]string // binary pkg name -> source pkg name +} + +// PackageDeps represents Debian package dependencies +type PackageDeps struct { + Package string + Source string + Version string + Depends []string + BuildDepends []string + Suggests []string + Recommends []string + Conflicts []string + Provides []string +} + +// NewDependencyResolver creates a comprehensive Debian dependency resolver +func NewDependencyResolver() *DependencyResolver { + return &DependencyResolver{ + resolver: NewResolver(), + cache: &dependencyCache{ + deps: make(map[string]*PackageDeps), + srcMap: make(map[string]string), + }, + httpClient: &http.Client{ + Timeout: 30 * time.Second, + }, + mirrors: []string{ + "https://deb.debian.org/debian", + "https://ftp.debian.org/debian", + "https://mirrors.kernel.org/debian", + }, + suites: []string{"sid", "unstable", "testing", "stable"}, + components: []string{"main", "contrib", "non-free"}, + arch: "amd64", + } +} + +// ResolveDependencies resolves all dependencies for a Debian binary package +func (r *DependencyResolver) ResolveDependencies(pkgName string) (*PackageDeps, error) { + // Check cache first + if cached := r.cache.get(pkgName); cached != nil { + return cached, nil + } + + // Try to find in Sources files (for build-deps) or Packages files (for runtime deps) + deps, err := r.resolveFromDebian(pkgName) + if err != nil { + return nil, err + } + + // Cache result + r.cache.set(pkgName, deps) + + return deps, nil +} + +// BinaryToSource maps a Debian binary package name to its source package name +func (r *DependencyResolver) BinaryToSource(binaryPkg string) (string, error) { + // Check cache + if src := r.cache.getSource(binaryPkg); src != "" { + return src, nil + } + + // Try built-in mappings first (fast path) + if src := mapBinaryToSourceFast(binaryPkg); src != "" { + r.cache.setSource(binaryPkg, src) + return src, nil + } + + // Look up in Debian Sources + srcInfo, err := r.resolver.ResolveSource(binaryPkg) + if err != nil { + return "", fmt.Errorf("cannot resolve source for %s: %w", binaryPkg, err) + } + + r.cache.setSource(binaryPkg, srcInfo.SourcePackage) + return srcInfo.SourcePackage, nil +} + +// ResolveDependencyChain resolves full dependency chain for a package +func (r *DependencyResolver) ResolveDependencyChain(rootPkg string, includeBuildDeps bool) ([]string, error) { + visited := make(map[string]bool) + result := make([]string, 0) + queue := []string{rootPkg} + + for len(queue) > 0 { + pkg := queue[0] + queue = queue[1:] + + if visited[pkg] { + continue + } + visited[pkg] = true + + // Get package dependencies + deps, err := r.ResolveDependencies(pkg) + if err != nil { + // Try to resolve as source package + srcPkg, srcErr := r.BinaryToSource(pkg) + if srcErr != nil { + continue // Skip unresolved + } + if !visited[srcPkg] { + result = append(result, srcPkg) + } + continue + } + + result = append(result, pkg) + + // Add runtime dependencies + for _, dep := range deps.Depends { + depName := r.extractDepName(dep) + if !visited[depName] { + queue = append(queue, depName) + } + } + + // Add build dependencies if requested + if includeBuildDeps { + for _, dep := range deps.BuildDepends { + depName := r.extractDepName(dep) + if !visited[depName] { + queue = append(queue, depName) + } + } + } + } + + return result, nil +} + +// extractDepName extracts package name from dependency string (e.g., "pkg (>= 1.0)" -> "pkg") +func (r *DependencyResolver) extractDepName(dep string) string { + // Remove version constraints + re := regexp.MustCompile(`^([a-zA-Z0-9+.-]+)`) + matches := re.FindStringSubmatch(dep) + if len(matches) > 1 { + return matches[1] + } + return dep +} + +// resolveFromDebian attempts to resolve dependencies from Debian repository +func (r *DependencyResolver) resolveFromDebian(pkgName string) (*PackageDeps, error) { + // Try to find binary package info from Packages files + for _, mirror := range r.mirrors { + for _, suite := range r.suites { + for _, component := range r.components { + deps, err := r.fetchBinaryPackageInfo(mirror, suite, component, pkgName) + if err == nil && deps != nil { + return deps, nil + } + } + } + } + + return nil, fmt.Errorf("package %s not found in Debian repositories", pkgName) +} + +// fetchBinaryPackageInfo fetches package info from Packages.xz +func (r *DependencyResolver) fetchBinaryPackageInfo(mirror, suite, component, pkgName string) (*PackageDeps, error) { + url := fmt.Sprintf("%s/dists/%s/%c%s/binary-%s/Packages.xz", + mirror, suite, component[0], component[1:], r.arch) + + resp, err := r.httpClient.Get(url) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("HTTP %d", resp.StatusCode) + } + + // Decompress xz + xzReader, err := xz.NewReader(resp.Body) + if err != nil { + return nil, err + } + + // Parse Packages file looking for our package + return r.parsePackagesFile(xzReader, pkgName) +} + +// parsePackagesFile parses Debian Packages file to find package info +func (dr *DependencyResolver) parsePackagesFile(reader io.Reader, targetPkg string) (*PackageDeps, error) { + scanner := bufio.NewScanner(reader) + var currentPkg *PackageDeps + inTarget := false + + for scanner.Scan() { + line := scanner.Text() + + // Empty line = end of paragraph + if line == "" { + if inTarget && currentPkg != nil { + return currentPkg, nil + } + currentPkg = nil + inTarget = false + continue + } + + // Check if this is our target package + if strings.HasPrefix(line, "Package: ") { + pkg := strings.TrimPrefix(line, "Package: ") + if pkg == targetPkg { + inTarget = true + currentPkg = &PackageDeps{Package: pkg} + } + } + + if !inTarget || currentPkg == nil { + continue + } + + // Parse fields + switch { + case strings.HasPrefix(line, "Source: "): + currentPkg.Source = strings.TrimPrefix(line, "Source: ") + case strings.HasPrefix(line, "Version: "): + currentPkg.Version = strings.TrimPrefix(line, "Version: ") + case strings.HasPrefix(line, "Depends: "): + currentPkg.Depends = dr.parseDepLine(strings.TrimPrefix(line, "Depends: ")) + case strings.HasPrefix(line, "Suggests: "): + currentPkg.Suggests = dr.parseDepLine(strings.TrimPrefix(line, "Suggests: ")) + case strings.HasPrefix(line, "Recommends: "): + currentPkg.Recommends = dr.parseDepLine(strings.TrimPrefix(line, "Recommends: ")) + case strings.HasPrefix(line, "Conflicts: "): + currentPkg.Conflicts = dr.parseDepLine(strings.TrimPrefix(line, "Conflicts: ")) + case strings.HasPrefix(line, "Provides: "): + currentPkg.Provides = dr.parseDepLine(strings.TrimPrefix(line, "Provides: ")) + } + } + + if err := scanner.Err(); err != nil { + return nil, err + } + + if inTarget && currentPkg != nil { + return currentPkg, nil + } + + return nil, fmt.Errorf("package not found") +} + +// parseDepLine parses dependency line (handles alternatives with |) +func (r *DependencyResolver) parseDepLine(line string) []string { + result := make([]string, 0) + + // Split by comma (separate dependencies) + parts := strings.Split(line, ",") + for _, part := range parts { + part = strings.TrimSpace(part) + if part != "" { + result = append(result, part) + } + } + + return result +} + +// Cache methods +func (c *dependencyCache) get(pkg string) *PackageDeps { + c.mu.RLock() + defer c.mu.RUnlock() + return c.deps[pkg] +} + +func (c *dependencyCache) set(pkg string, deps *PackageDeps) { + c.mu.Lock() + defer c.mu.Unlock() + c.deps[pkg] = deps +} + +func (c *dependencyCache) getSource(binary string) string { + c.mu.RLock() + defer c.mu.RUnlock() + return c.srcMap[binary] +} + +func (c *dependencyCache) setSource(binary, source string) { + c.mu.Lock() + defer c.mu.Unlock() + c.srcMap[binary] = source +} + +// mapBinaryToSourceFast provides fast static mappings for common packages +func mapBinaryToSourceFast(binary string) string { + // Comprehensive mapping of Debian binary packages to source packages + mappings := map[string]string{ + // Essential build tools + "cmake": "cmake", + "ninja-build": "ninja-build", + "meson": "meson", + "pkg-config": "pkgconf", + "pkgconf": "pkgconf", + + // Compilers + "gcc": "gcc-defaults", + "g++": "gcc-defaults", + "clang": "llvm-defaults", + + // SSL/TLS + "libssl-dev": "openssl", + "libssl3": "openssl", + "openssl": "openssl", + + // Compression + "zlib1g-dev": "zlib", + "zlib1g": "zlib", + "libbz2-dev": "bzip2", + "liblzma-dev": "xz-utils", + "libzstd-dev": "zstd", + + // XML + "libxml2-dev": "libxml2", + "libxml2": "libxml2", + "libxslt1-dev": "libxslt", + + // JSON + "libjson-c-dev": "json-c", + "libjson-c5": "json-c", + + // cURL + "libcurl4-openssl-dev": "curl", + "libcurl4-gnutls-dev": "curl", + "curl": "curl", + + // Database + "libsqlite3-dev": "sqlite3", + "libsqlite3-0": "sqlite3", + + // Terminal/UI + "libncurses-dev": "ncurses", + "libncurses6": "ncurses", + "libreadline-dev": "readline", + "libreadline8": "readline", + + // Image + "libpng-dev": "libpng1.6", + "libpng16-16": "libpng1.6", + "libjpeg-dev": "libjpeg-turbo", + "libtiff-dev": "tiff", + + // Crypto + "libgcrypt20-dev": "libgcrypt20", + "libgpg-error-dev": "libgpg-error", + + // GNUTLS + "libgnutls28-dev": "gnutls28", + + // PCRE + "libpcre3-dev": "pcre3", + "libpcre2-dev": "pcre2", + + // Expat + "libexpat1-dev": "expat", + + // FFI + "libffi-dev": "libffi", + + // UUID + "libuuid-dev": "util-linux", + + // Git + "git": "git", + + // Flex/Bison + "flex": "flex", + "bison": "bison", + "m4": "m4", + + // Autotools + "autoconf": "autoconf", + "automake": "automake", + "libtool": "libtool", + + // Gettext + "gettext": "gettext", + "libgettextpo-dev": "gettext", + + // Lua + "liblua5.1-dev": "lua5.1", + "liblua5.4-dev": "lua5.4", + + // Python + "python3-dev": "python3-defaults", + "python3": "python3-defaults", + + // Perl + "perl": "perl", + + // Procps + "procps": "procps-ng", + + // Iconv + "libiconv-hook-dev": "libiconv", + + // ICU + "libicu-dev": "icu", + + // HarfBuzz + "libharfbuzz-dev": "harfbuzz", + + // FontConfig + "libfontconfig-dev": "fontconfig", + + // Freetype + "libfreetype-dev": "freetype", + + // Cairo + "libcairo2-dev": "cairo", + + // Pango + "libpango1.0-dev": "pango1.0", + + // GDK-Pixbuf + "libgdk-pixbuf-2.0-dev": "gdk-pixbuf", + + // GLib + "libglib2.0-dev": "glib2.0", + + // GObject introspection + "gobject-introspection": "gobject-introspection", + + // ATK + "libatk1.0-dev": "atk1.0", + + // GTK + "libgtk-3-dev": "gtk+3.0", + "libgtk-4-dev": "gtk4", + + // GLibmm + "libglibmm-2.4-dev": "glibmm2.4", + + // X11 + "libx11-dev": "libx11", + "libxext-dev": "libxext", + "libxrender-dev": "libxrender", + "libxft-dev": "libxft", + "libxpm-dev": "libxpm", + "libxmu-dev": "libxmu", + "libxaw7-dev": "libxaw", + "libxfixes-dev": "libxfixes", + "libxinerama-dev": "libxinerama", + "libxrandr-dev": "libxrandr", + "libxcursor-dev": "libxcursor", + "libxcomposite-dev": "libxcomposite", + "libxdamage-dev": "libxdamage", + "libxkbfile-dev": "libxkbfile", + + // XCB + "libxcb1-dev": "libxcb", + "libxcb-keysyms1-dev": "xcb-util-keysyms", + + // Xt + "libxt-dev": "libxt", + + // SM/ICE + "libsm-dev": "libsm", + "libice-dev": "libice", + + // Xtrans + "libxtrans-dev": "libxtrans", + + // X11proto + "x11proto-dev": "xorgproto", + "xorg-sgml-doctools": "xorg-sgml-doctools", + + // Wayland + "libwayland-dev": "wayland", + "wayland-protocols": "wayland-protocols", + + // OpenGL + "libgl-dev": "libglvnd", + "libgl1-mesa-dev": "mesa", + "libegl1-mesa-dev": "mesa", + "libgbm-dev": "mesa", + + // Vulkan + "libvulkan-dev": "vulkan-loader", + + // Shaderc + "libshaderc-dev": "shaderc", + + // SPIRV + "libspirv-tools-dev": "spirv-tools", + + // PulseAudio + "libpulse-dev": "pulseaudio", + + // ALSA + "libasound2-dev": "alsa-lib", + + // JACK + "libjack-dev": "jack-audio-connection-kit", + + // PipeWire + "libpipewire-0.3-dev": "pipewire", + + // D-Bus + "libdbus-1-dev": "dbus", + + // GMP + "libgmp-dev": "gmp", + + // MPFR + "libmpfr-dev": "mpfr", + + // MPC + "libmpc-dev": "mpc", + + // GPGME + "libgpgme-dev": "gpgme1.0", + + // Assuan + "libassuan-dev": "libassuan", + + // KSBA + "libksba-dev": "libksba", + + // NTBTLS + "libntbtls-dev": "ntbtls", + + // OpenLDAP + "libldap2-dev": "openldap", + + // SASL + "libsasl2-dev": "cyrus-sasl2", + + // Kerberos + "libkrb5-dev": "krb5", + + // Keyutils + "libkeyutils-dev": "keyutils", + + // Nettle + "libnettle-dev": "nettle", + + // Hogweed + "libhogweed6": "nettle", + + // TASN1 + "libtasn1-6-dev": "libtasn1-6", + + // IDN2 + "libidn2-dev": "libidn2", + + // Unistring + "libunistring-dev": "libunistring", + + // PSL + "libpsl-dev": "libpsl", + + // BROTLI + "libbrotli-dev": "brotli", + + // NGHTTP2 + "libnghttp2-dev": "nghttp2", + + // Systemd + "libsystemd-dev": "systemd", + + // Elogind + "libelogind-dev": "elogind", + + // UPower + "libupower-glib-dev": "upower", + + // UDisks + "libudisks2-dev": "udisks2", + + // Polkit + "libpolkit-gobject-1-dev": "policykit-1", + + // ConsoleKit + "libck-connector-dev": "consolekit", + + // UDEV + "libudev-dev": "systemd", + + // Eudev + "libeudev-dev": "eudev", + + // KMod + "libkmod-dev": "kmod", + + // PCIUtils + "libpci-dev": "pciutils", + + // USBUtils + "libusb-1.0-0-dev": "usbutils", + + // Libusb + "libusb-1.0-dev": "libusb-1.0", + + // HIDAPI + "libhidapi-dev": "hidapi", + + // SDL2 + "libsdl2-dev": "sdl2", + + // OpenAL + "libopenal-dev": "openal-soft", + + // Ogg + "libogg-dev": "libogg", + + // Vorbis + "libvorbis-dev": "libvorbis", + + // FLAC + "libflac-dev": "flac", + + // Opus + "libopus-dev": "opus", + + // Theora + "libtheora-dev": "libtheora", + + // VPX + "libvpx-dev": "libvpx", + + // AOM + "libaom-dev": "aom", + + // DAV1D + "libdav1d-dev": "dav1d", + + // FFmpeg + "libavcodec-dev": "ffmpeg", + "libavformat-dev": "ffmpeg", + "libavutil-dev": "ffmpeg", + + // X264 + "libx264-dev": "x264", + + // X265 + "libx265-dev": "x265", + + // WebP + "libwebp-dev": "libwebp", + + // OpenEXR + "libopenexr-dev": "openexr", + + // Imath + "libimath-dev": "imath", + + // OpenJPEG + "libopenjp2-7-dev": "openjpeg2", + + // LittleCMS + "liblcms2-dev": "lcms2", + + // "libreadline-dev": "readline", + + // History + "libhistory-dev": "readline", + + // Termcap + "libtinfo-dev": "ncurses", + + // Curses + "libcurses-dev": "ncurses", + + // Panel + "libpanel-dev": "ncurses", + + // Menu + "libmenu-dev": "ncurses", + + // Form + "libform-dev": "ncurses", + + // SLANG + "libslang2-dev": "slang2", + + // Newt + "libnewt-dev": "newt", + + // CDK + "libcdk5-dev": "cdk", + + // ADNS + "libadns1-dev": "adns", + + // Anet + "libanet-dev": "anet", + + // APR + "libapr1-dev": "apr", + + // APR-Util + "libaprutil1-dev": "apr-util", + + // ASpell + "libaspell-dev": "aspell", + + // Hunspell + "libhunspell-dev": "hunspell", + + // HSpell + "libhspell-dev": "hspell", + + // Voikko + "libvoikko-dev": "libvoikko", + + // Zemberek + "libzemberek-dev": "zemberek", + + // Zinnia + "libzinnia-dev": "zinnia", + + // Teckit + "libteckit-dev": "teckit", + + // Graphite + "libgraphite2-dev": "graphite2", + + // Raptor + "libraptor2-dev": "raptor2", + + // Rasqal + "librasqal3-dev": "rasqal", + + // Redland + "librdf0-dev": "redland", + + // Serd + "libserd-dev": "serd", + + // Sord + "libsord-dev": "sord", + + // SRATOM + "libsratom-dev": "sratom", + + // LILV + "liblilv-dev": "lilv", + + // LV2 + "lv2-dev": "lv2", + + // SUIL + "libsuil-dev": "suil", + + // DSSI + "libdssi-dev": "dssi", + + // LADSPA + "libladspa-sdk-dev": "ladspa-sdk", + + // VST + "vst3sdk-dev": "vst3sdk", + + // CLAP + "clap-dev": "clap", + + // BS2B + "libbs2b-dev": "libbs2b", + + // LADSPA (again) + "ladspa-sdk": "ladspa-sdk", + + // CAPS + "caps": "caps", + + // TAP + "tap-plugins": "tap-plugins", + + // CMT + "cmt": "cmt", + + // SWH + "swh-plugins": "swh-plugins", + + // FFAUDIO + "libffado-dev": "libffado", + + // ALSA (again) + "libasound-dev": "alsa-lib", + + // FireWire + "libraw1394-dev": "libraw1394", + + // IEC61883 + "libiec61883-dev": "libiec61883", + + // AV1394 + "libavc1394-dev": "libavc1394", + + // DC1394 + "libdc1394-dev": "libdc1394", + + // GLIB (already defined above) + // "libglib2.0-dev": "glib2.0", + + // GObject (again) + "libgobject2.0-dev": "glib2.0", + + // GModule (again) + "libgmodule2.0-dev": "glib2.0", + + // GIO (again) + "libgio2.0-dev": "glib2.0", + + // GThread (again) + "libgthread2.0-dev": "glib2.0", + + // Mount + "libmount-dev": "util-linux", + + // BLKID + "libblkid-dev": "util-linux", + + // UUID (already defined above) + // "libuuid-dev": "util-linux", + + // SELinux + "libselinux1-dev": "selinux", + + // SEManage + "libsemanage-dev": "semanage", + + // SECureDB + "libsecomp-dev": "libseccomp", + + // Audit + "libauparse-dev": "audit", + + // E2FSProgs + "libext2fs-dev": "e2fsprogs", + + // ComErr + "libcom-err2-dev": "e2fsprogs", + + // SS + "libss2-dev": "e2fsprogs", + + // Magic + "libmagic-dev": "file", + + // ACap + "libcap-dev": "libcap2", + + // ACap-NG + "libcap-ng-dev": "libcap-ng", + + // AppArmor + "libapparmor-dev": "apparmor", + + // Tomoyo + "libtomoyotools-dev": "tomoyo-tools", + + // SMACK + "libsmack-dev": "smack", + + // TOMOYO (again) + "tomoyo-tools": "tomoyo-tools", + + // IMA-EVM + "ima-evm-utils": "ima-evm-utils", + + // TSS2 + "libtss2-dev": "tpm2-tss", + + // FAPI + "libfapi-dev": "tpm2-tss", + + // TCTI + "libtcti-dev": "tpm2-tss", + + // ESYS + "libesys-dev": "tpm2-tss", + + // MU + "libmu-dev": "mailutils", + + // Guile + "guile-3.0-dev": "guile-3.0", + + // GMP (already defined at line 346) + // "libgmp-dev": "gmp", + + // Ncurses (variant) + "libncursesw5-dev": "ncurses", + + // Readline (already defined at line 369) + // "libreadline-dev": "readline", + + // Texinfo + "texinfo": "texinfo", + + // TexLive + "texlive": "texlive-base", + + // Doxygen + "doxygen": "doxygen", + + // Graphviz + "graphviz-dev": "graphviz", + + // PLplot + "libplplot-dev": "plplot", + + // GSL + "libgsl-dev": "gsl", + + // FFTW + "libfftw3-dev": "fftw3", + + // BLAS + "libblas-dev": "blas", + + // LAPACK + "liblapack-dev": "lapack", + + // ATLAS + "libatlas-base-dev": "atlas", + + // OpenBLAS + "libopenblas-dev": "openblas", + + // SuiteSparse + "libsuitesparse-dev": "suitesparse", + + // ARPACK + "libarpack2-dev": "arpack", + + // PARPACK + "libparpack2-dev": "arpack", + + // SuperLU + "libsuperlu-dev": "superlu", + + // METIS + "libmetis-dev": "metis", + + // Scotch + "libscotch-dev": "scotch", + + // MUMPS + "libmumps-dev": "mumps", + + // Hypre + "libhypre-dev": "hypre", + + // PETSc + "libpetsc-dev": "petsc", + + // SLEPc + "libslepc-dev": "slepc", + + // Trilinos + "trilinos-all-dev": "trilinos", + + // Boost + "libboost-all-dev": "boost-defaults", + + // Eigen3 + "libeigen3-dev": "eigen3", + + // Dune + "libdune-common-dev": "dune-common", + + // FEniCS + "fenics": "fenics", + + // Deal.II + "libdeal.ii-dev": "deal.ii", + + // CGAL + "libcgal-dev": "cgal", + + // PPL + "libppl-dev": "ppl", + + // NTL + "libntl-dev": "ntl", + + // FLINT + "libflint-dev": "flint", + + // Arb + "libarb-dev": "arb", + + // Antic + "libantic-dev": "antic", + + // Calcium + "libcalcium-dev": "calcium", + + // NF + "libnf-dev": "nf", + + // GF2X + "libgf2x-dev": "gf2x", + + // M4RI + "libm4ri-dev": "m4ri", + + // M4RIE + "libm4rie-dev": "m4rie", + + // LinBox + "liblinbox-dev": "linbox", + + // IML + "libiml-dev": "iml", + + // BLAD + "libblad-dev": "blad", + + // CADO + "cado-nfs-dev": "cado-nfs", + + // PARI + "libpari-dev": "pari", + + // Symmetrica + "symmetrica-dev": "symmetrica", + + // Lidia + "liblidia-dev": "lidia", + + // NTL (already defined above) + // "libntl-dev": "ntl", + + // OpenSSL (already defined above) + // "libssl-dev": "openssl", + + // OpenSSH + "libssh-dev": "libssh", + + // OpenSSH2 + "libssh2-1-dev": "libssh2", + + // Neon + "libneon27-dev": "neon27", + + // Serf + "libserf-1-dev": "serf", + + // Subversion + "libsvn-dev": "subversion", + + // RHash + "librhash-dev": "rhash", + + // XXHASH + "libxxhash-dev": "xxhash", + + // HighwayHash + "libhighwayhash-dev": "highwayhash", + + // CityHash + "libcityhash-dev": "cityhash", + + // MurmurHash + "libmurmurhash-dev": "murmurhash", + + // SpookyHash + "libspookyhash-dev": "spookyhash", + + // FarmHash + "libfarmhash-dev": "farmhash", + + // MetroHash + "libmetrohash-dev": "metrohash", + + // t1ha + "libt1ha-dev": "t1ha", + + // PRVHASH + "libprvhash-dev": "prvhash", + + // Wyhash + "libwyhash-dev": "wyhash", + + // rapidhash + "librapidhash-dev": "rapidhash", + + // SHA3 + "libsha3-dev": "sha3", + + // Blake2 + "libblake2-dev": "blake2", + + // Blake3 + "libblake3-dev": "blake3", + + // KangarooTwelve + "libk12-dev": "k12", + + // SHAKE + "libshake-dev": "sha3", + + // TupleHash + "libtuplehash-dev": "sha3", + + // ParallelHash + "libparallelhash-dev": "sha3", + + // cSHAKE + "libcshake-dev": "sha3", + + // TurboSHAKE + "libturboshake-dev": "turbo", + + // Ascon + "libascon-dev": "ascon", + + // ISAP + "libisap-dev": "isap", + + // DryGascon + "libdrygascon-dev": "drygascon", + + // Gimli + "libgimli-dev": "gimli", + + // Xoodoo + "libxoodoo-dev": "xoodoo", + + // Keccak + "libkeccak-dev": "keccak", + + // FNV + "libfnv-dev": "fnv", + + // Murmur (already defined above) + "libmurmurhash3-dev": "murmurhash", + + // CityHash (already defined above) + // "libcityhash-dev": "cityhash", + } + + return mappings[binary] +}