Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
bin
doc/repl/glj.wasm
/bin
/doc/repl/glj.wasm
/report.html
.direnv

# useful to symlink in for context
Expand Down
183 changes: 128 additions & 55 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,10 +1,29 @@

CLOJURE_STDLIB_VERSION := clojure-1.12.1
STDLIB_ORIGINALS_DIR := scripts/rewrite-core/originals
STDLIB_ORIGINALS := $(shell find $(STDLIB_ORIGINALS_DIR) -name '*.clj')
STDLIB := $(STDLIB_ORIGINALS:scripts/rewrite-core/originals/%=%)
STDLIB_ORIGINALS := $(addprefix scripts/rewrite-core/originals/,$(STDLIB))
STDLIB_TARGETS := $(addprefix pkg/stdlib/clojure/,$(STDLIB:.clj=.glj))
# Usage:
# make clean all test GO-VERSION=1.25.1

SHELL := bash

GO-VERSION ?= 1.19.3
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why the switch from SNAKE_CASE to KEBAB-CASE? i've seen kebab for targets but snake seems like the norm for variables

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know if you read through my explanations in the commit messages, but 7f2d72d

Makefiles inherit from the env which only allows SNAKE_CASE. KEBAB-BASE is only allowed in Makefile vars, not env ones, so it makes the vars ownership very clear.

You can override vars from the command line like this:

ENV_VAR=foo make all MAKE-VAR=bar

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also in commit 1b0f4cd I made almost all of the SINGLEWORD Makefile vars into MULTI-WORD vars, to drive home that they don't ever come from (can't come from) the environment.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fair point! don't feel strongly, but i wanted to understand the motivation and i failed to read the (useful!) commit messages :)

CLOJURE-VERSION ?= 1.12.1

CLOJURE-STDLIB-VERSION := clojure-$(CLOJURE-VERSION)
STDLIB-ORIGINALS-DIR := scripts/rewrite-core/originals
STDLIB-ORIGINALS := $(wildcard $(STDLIB-ORIGINALS-DIR)/*.clj)
STDLIB-NAMES := $(STDLIB-ORIGINALS:scripts/rewrite-core/originals/%=%)
STDLIB-ORIGINALS := $(STDLIB-NAMES:%=scripts/rewrite-core/originals/,%)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this line needed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

STDLIB-TARGETS := $(addprefix pkg/stdlib/clojure/,$(STDLIB-NAMES:.clj=.glj))

AOT-NAMESPACES := \
clojure.core \
clojure.core.async \
clojure.string \
clojure.template \
clojure.test \
clojure.uuid \
clojure.walk \
glojure.go.io \
glojure.go.types \
$(EXTRA-AOT-NAMESPACES)

OS-TYPE := $(shell bash -c 'echo $$OSTYPE')
OS-NAME := \
Expand All @@ -23,88 +42,142 @@ OA-linux-arm64 := linux_arm64
OA-linux-int64 := linux_amd64
OA-macos-arm64 := darwin_arm64
OA-macos-int64 := darwin_amd64
OA := $(OA-$(OS-ARCH))
GLJ := bin/$(OA)/glj
GLJ-CMD := bin/$(OA-$(OS-ARCH))/glj
endif
endif

TEST_FILES := $(shell find ./test -name '*.glj' | sort)
TEST_TARGETS := $(addsuffix .test,$(TEST_FILES))
TEST-GLJ-DIR := test/glojure
TEST-GLJ-FILES := $(shell find $(TEST-GLJ-DIR) -name '*.glj' | sort)
TEST-GLJ-TARGETS := $(addsuffix .test,$(TEST-GLJ-FILES))
TEST-SUITE-DIR := test/clojure-test-suite
TEST-SUITE-FILE := test-glojure.glj

GO-PLATFORMS := \
darwin_arm64 \
darwin_amd64 \
linux_arm64 \
linux_amd64 \
windows_arm \
windows_amd64 \
js_wasm \
$(EXTRA-GO-PLATFORMS)

GLJ-IMPORTS=$(foreach platform,$(GO-PLATFORMS) \
,pkg/gen/gljimports/gljimports_$(platform).go)

GOPLATFORMS := darwin_arm64 darwin_amd64 linux_arm64 linux_amd64 windows_amd64 windows_arm js_wasm
GLJIMPORTS=$(foreach platform,$(GOPLATFORMS),pkg/gen/gljimports/gljimports_$(platform).go)
# wasm should have .wasm suffix; others should not
BINS=$(foreach platform,$(GOPLATFORMS),bin/$(platform)/glj$(if $(findstring wasm,$(platform)),.wasm,))
GLJ-BINS=$(foreach platform,$(GO-PLATFORMS) \
,bin/$(platform)/glj$(if $(findstring wasm,$(platform)),.wasm,))

GO-CMD := go$(GO-VERSION)

ALL-TARGETS := \
$(if $(force),update-clojure-sources) \
stdlib-targets \
generate \
aot \
glj-imports \
glj-bins \

# eventually, support multiple minor versions
GO_VERSION := 1.19.3
GO_CMD := go$(GO_VERSION)
#-------------------------------------------------------------------------------
default: all

.PHONY: all
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we keep the .PHONY declarations for the targets that don't produce files?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. .PHONY is not only noisy and useless. I found out from this refactor that it can be dangerous.

Here https://github.com/glojurelang/glojure/blob/main/Makefile#L44 all deps on go-generate but that target doesn't exist. The Makefile should error but it doesn't because https://github.com/glojurelang/glojure/blob/main/Makefile#L52 marks go-generate as PHONY.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See commit messages:

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fwiw i've been bitten by missing .PHONY before, but i don't feel strongly. happy to run with this

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Me too, but those bites are rare and almost instantly recognized.
The bite I described (where PHONY masked a typo) remained hidden for a while.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's an interesting fact about PHONY you might now know...

You can put all your PHONY targets on one line at the end of the file if you want:

.PHONY: all test clean etc

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can put all your PHONY targets on one line at the end of the file if you want:

ah, i'm familiar with that, but i prefer to keep them adjacent to their targets so it's harder for them to get out of sync — which i managed to do with one of the targets anyway :)

all: gocmd $(STDLIB_TARGETS) go-generate aot $(GLJIMPORTS) $(BINS)
# Dummy target for commands like:
# make all force=1
# make stdlib-targets force=1
force:

all: $(ALL-TARGETS)

.PHONY: gocmd
gocmd:
@$(GO_CMD) version 2>&1 > /dev/null || \
(go install "golang.org/dl/$(GO_CMD)@latest" && \
$(GO_CMD) download > /dev/null && $(GO_CMD) version > /dev/null)
@$(GO-CMD) version &> /dev/null || { \
(go install "golang.org/dl/$(GO-CMD)@latest" && \
$(GO-CMD) download > /dev/null && \
$(GO-CMD) version > /dev/null); }

stdlib-targets: $(STDLIB-TARGETS)

.PHONY: go-generate
generate:
@go generate ./...
go generate ./...

.PHONY: aot
aot: gocmd $(STDLIB_TARGETS)
@echo "(map compile '[clojure.core clojure.core.async clojure.string clojure.template clojure.test clojure.uuid clojure.walk glojure.go.types glojure.go.io])" | \
GLOJURE_USE_AOT=false GLOJURE_STDLIB_PATH=./pkg/stdlib $(GO_CMD) run -tags glj_no_aot_stdlib ./cmd/glj
aot: gocmd $(STDLIB-TARGETS)
GLOJURE_USE_AOT=false \
GLOJURE_STDLIB_PATH=./pkg/stdlib \
$(GO-CMD) run -tags glj_no_aot_stdlib ./cmd/glj \
<<<"(map compile '[$(AOT-NAMESPACES)])"

.PHONY: build
build: $(GLJ)
glj-imports: $(GLJ-IMPORTS)

.PHONY: clean
clean:
$(RM) -r bin/
glj-bins: $(GLJ-BINS)

pkg/gen/gljimports/gljimports_%.go: ./scripts/gen-gljimports.sh ./cmd/gen-import-interop/main.go ./internal/genpkg/genpkg.go \
$(wildcard ./pkg/lang/*.go) $(wildcard ./pkg/runtime/*.go)
build: $(GLJ-CMD)

clean:
$(RM) report.html
$(RM) -r bin/ scripts/rewrite-core/.cpcache/

pkg/gen/gljimports/gljimports_%.go: \
./scripts/gen-gljimports.sh \
./cmd/gen-import-interop/main.go \
./internal/genpkg/genpkg.go \
$(wildcard ./pkg/lang/*.go) \
$(wildcard ./pkg/runtime/*.go) \
$(if $(force),force)
@echo "Generating $@"
@./scripts/gen-gljimports.sh $@ $* $(GO_CMD)
./scripts/gen-gljimports.sh $@ $* $(GO-CMD)

pkg/stdlib/clojure/%.glj: scripts/rewrite-core/originals/%.clj scripts/rewrite-core/run.sh scripts/rewrite-core/rewrite.clj
pkg/stdlib/clojure/%.glj: \
scripts/rewrite-core/originals/%.clj \
scripts/rewrite-core/run.sh \
scripts/rewrite-core/rewrite.clj \
$(if $(force),force)
@echo "Rewriting $< to $@"
@mkdir -p $(dir $@)
@scripts/rewrite-core/run.sh $< > $@
scripts/rewrite-core/run.sh $< > $@

bin/%/glj: generate $(wildcard ./cmd/glj/*.go) $(wildcard ./pkg/**/*.go) $(wildcard ./internal/**/*.go)
bin/%/glj: generate \
$(wildcard ./cmd/glj/*.go) \
$(wildcard ./pkg/**/*.go) \
$(wildcard ./internal/**/*.go) \
$(if $(force),force)
@echo "Building $@"
@mkdir -p $(dir $@)
@scripts/build-glj.sh $@ $*
scripts/build-glj.sh $@ $*

bin/%/glj.wasm: $(wildcard ./cmd/glj/*.go) $(wildcard ./pkg/**/*.go) $(wildcard ./internal/**/*.go)
bin/%/glj.wasm: \
$(wildcard ./cmd/glj/*.go) \
$(wildcard ./pkg/**/*.go) \
$(wildcard ./internal/**/*.go) \
$(if $(force),force)
@echo "Building $@"
@mkdir -p $(dir $@)
@scripts/build-glj.sh $@ $*
scripts/build-glj.sh $@ $*

.PHONY: vet
vet:
@go vet ./...

.PHONY: $(TEST_TARGETS)
$(TEST_TARGETS): gocmd $(GLJ)
@$(GLJ) $(basename $@)
go vet ./...

.PHONY: test
test: $(TEST_TARGETS) # vet - vet is disabled until we fix errors in generated code
@cd test/clojure-test-suite && \
../../$(GLJ) test-glojure.glj --expect-failures 38 --expect-errors 151 2>/dev/null
# vet is disabled until we fix errors in generated code
test: test-glj test-suite # vet

test-glj: $(TEST-GLJ-TARGETS)

test-suite: $(GLJ-CMD)
cd $(TEST-SUITE-DIR) && \
$(abspath $<) $(TEST-SUITE-FILE) \
--expect-failures 38 \
--expect-errors 151 \
2>/dev/null

$(TEST-GLJ-TARGETS): $(GLJ-CMD)
$< $(basename $@)

.PHONY: format
format:
@if go fmt ./... | grep -q ''; then \
echo "Files were formatted. Please commit the changes."; \
exit 1; \
fi

.PHONY: update-clojure-sources
update-clojure-sources:
@scripts/rewrite-core/update-clojure-sources.sh $(CLOJURE_STDLIB_VERSION)
scripts/rewrite-core/update-clojure-sources.sh \
$(CLOJURE-STDLIB-VERSION)
24 changes: 12 additions & 12 deletions pkg/ast/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,21 +190,21 @@ type (
}

CaseNode struct {
Test *Node // The expression to test
Shift int64 // Bit shift for hash compaction
Mask int64 // Bit mask for hash compaction
TestType interface{} // Keyword: :int, :hash-identity, or :hash-equiv
SwitchType interface{} // Keyword: :compact or :sparse
Default *Node // Default expression
Entries []CaseEntry // Case entries
SkipCheck map[int64]bool // Set of keys with collisions
Test *Node // The expression to test
Shift int64 // Bit shift for hash compaction
Mask int64 // Bit mask for hash compaction
TestType interface{} // Keyword: :int, :hash-identity, or :hash-equiv
SwitchType interface{} // Keyword: :compact or :sparse
Default *Node // Default expression
Entries []CaseEntry // Case entries
SkipCheck map[int64]bool // Set of keys with collisions
}

CaseEntry struct {
Key int64 // Map key (int value or shifted/masked hash)
TestConstant *Node // Original test constant (nil for collisions)
ResultExpr *Node // Result expression or condp for collisions
HasCollision bool // Whether this is a collision case
Key int64 // Map key (int value or shifted/masked hash)
TestConstant *Node // Original test constant (nil for collisions)
ResultExpr *Node // Result expression or condp for collisions
HasCollision bool // Whether this is a collision case
}

TheVarNode struct {
Expand Down
4 changes: 2 additions & 2 deletions pkg/stdlib/clojure/walk/loader.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading