Skip to content

Instantly share code, notes, and snippets.

@jakubtomsu
Created February 10, 2026 18:30
Show Gist options
  • Select an option

  • Save jakubtomsu/730a4343ba0137388d789f507857b9d7 to your computer and use it in GitHub Desktop.

Select an option

Save jakubtomsu/730a4343ba0137388d789f507857b9d7 to your computer and use it in GitHub Desktop.
package import_scan
import "core:strings"
import "core:strconv"
import "core:path/filepath"
import "core:fmt"
import "core:os/os2"
import "core:odin/ast"
import "core:odin/parser"
import "core:odin/tokenizer"
_collection_paths: map[string]string
main :: proc() {
if len(os2.args) <= 1 {
fmt.println("Error: please specify root package path")
}
_collection_paths["base"] = filepath.join({ODIN_ROOT,"base"})
_collection_paths["core"] = filepath.join({ODIN_ROOT,"core"})
_collection_paths["vendor"] = filepath.join({ODIN_ROOT, "vendor"})
root_pkg := os2.args[1]
pkgs: map[string]^ast.Package
parse_package_tree(&pkgs, root_pkg, depth = 0)
fmt.eprintln("\nRESULT:")
for name, pkg in pkgs {
fmt.eprintfln("PKG %s", name)
for name, file in pkg.files {
fmt.eprintfln(" FILE %s", name)
for imp in file.imports {
path, _ := resolve_import(imp)
fmt.eprintfln(" import %s", path)
}
}
}
fmt.println("digraph deps {")
fmt.println("\tnode [shape=box];")
for name, pkg in pkgs {
str, _ := strings.replace_all(name, "\\", "/")
id := path_id(str)
fmt.printfln("\t%s [label=\"%s\"];", id, str)
imports: map[string]struct{}
for name, file in pkg.files {
for imp in file.imports {
path, _ := resolve_import(imp)
imports[path] = {}
}
}
for imp in imports {
fmt.printfln("\t%s -> %s;", id, path_id(imp))
}
}
fmt.println("}")
}
path_id :: proc(path: string) -> string {
p := path
p, _ = strings.replace_all(p, "/", "_")
p, _ = strings.replace_all(p, "\\", "_")
p, _ = strings.replace_all(p, ".", "_")
p, _ = strings.replace_all(p, ":", "_")
p, _ = strings.replace_all(p, "-", "_")
return p
}
parse_package_tree :: proc(pkgs: ^map[string]^ast.Package, path: string, depth: int) {
assert(depth < 30)
if path in pkgs {
return
}
pkg, pkg_ok := parser.parse_package_from_path(path)
if !pkg_ok {
fmt.eprintfln("Failed to parse package at '%s'", path)
os2.exit(1)
}
// fmt.println(path, pkg.name, pkg.fullpath)
pkgs[pkg.fullpath] = pkg
for name, file in pkg.files {
for imp in file.imports {
path := resolve_import(imp) or_continue
parse_package_tree(pkgs, path, depth + 1)
}
}
}
resolve_import :: proc(imp: ^ast.Import_Decl) -> (result: string, keep_resolving: bool) {
relative := unquote(imp.relpath.text)
if colon_index := strings.index_byte(relative, ':'); colon_index >= 0 {
coll := relative[:colon_index]
name := relative[colon_index + 1:]
coll_path := _collection_paths[coll]
result = filepath.join({coll_path, name})
switch coll {
case "base":
return result, false
}
} else {
result = unquote(imp.fullpath)
}
return result, true
}
unquote :: proc(str: string) -> string {
res, _, _ := strconv.unquote_string(str, context.temp_allocator)
return res
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment