From 3ebbbeead35b8c44b065f80f1c48aa67f71deee8 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sat, 8 Feb 2025 17:40:31 +0900 Subject: [PATCH] internal/shaderlister: add go:embeddedshader directive for embedded files Updates #3157 --- examples/flappy/crt.go | 2 + examples/shader/chromaticaberration.go | 2 + examples/shader/default.go | 2 + examples/shader/dissolve.go | 2 + examples/shader/lighting.go | 3 +- examples/shader/radialblur.go | 2 + examples/shader/texel.go | 2 + examples/shader/water.go | 2 + internal/shaderlister/main.go | 49 ++++++++++++++----- internal/shaderlister/shaderlister_test.go | 37 ++++++++++---- internal/shaderlister/shaderlistertest/def.go | 12 ++++- .../shaderlistertest/embed2_kage.go | 21 ++++++++ .../shaderlistertest/embed_kage.go | 21 ++++++++ .../shaderlistertest/embed_notkage.go | 21 ++++++++ .../shaderlistertest/resource/embed_kage.go | 21 ++++++++ .../resource/embed_notkage.go | 21 ++++++++ 16 files changed, 197 insertions(+), 23 deletions(-) create mode 100644 internal/shaderlister/shaderlistertest/embed2_kage.go create mode 100644 internal/shaderlister/shaderlistertest/embed_kage.go create mode 100644 internal/shaderlister/shaderlistertest/embed_notkage.go create mode 100644 internal/shaderlister/shaderlistertest/resource/embed_kage.go create mode 100644 internal/shaderlister/shaderlistertest/resource/embed_notkage.go diff --git a/examples/flappy/crt.go b/examples/flappy/crt.go index 5f2d8d831..ff38b3c49 100644 --- a/examples/flappy/crt.go +++ b/examples/flappy/crt.go @@ -14,6 +14,8 @@ //go:build ignore +//ebitengine:embeddedshader + //kage:unit pixels // Reference: a public domain CRT effect diff --git a/examples/shader/chromaticaberration.go b/examples/shader/chromaticaberration.go index f5689a99a..abbf49ced 100644 --- a/examples/shader/chromaticaberration.go +++ b/examples/shader/chromaticaberration.go @@ -14,6 +14,8 @@ //go:build ignore +//ebitengine:embeddedshader + //kage:unit pixels package main diff --git a/examples/shader/default.go b/examples/shader/default.go index 1c7f70197..4d5387a59 100644 --- a/examples/shader/default.go +++ b/examples/shader/default.go @@ -14,6 +14,8 @@ //go:build ignore +//ebitengine:embeddedshader + //kage:unit pixels package main diff --git a/examples/shader/dissolve.go b/examples/shader/dissolve.go index 006061a26..6388d0d19 100644 --- a/examples/shader/dissolve.go +++ b/examples/shader/dissolve.go @@ -14,6 +14,8 @@ //go:build ignore +//ebitengine:embeddedshader + //kage:unit pixels package main diff --git a/examples/shader/lighting.go b/examples/shader/lighting.go index 020001f6e..9d6761cd0 100644 --- a/examples/shader/lighting.go +++ b/examples/shader/lighting.go @@ -14,7 +14,8 @@ //go:build ignore -// Specify the 'pixel' mode. +//ebitengine:embeddedshader + //kage:unit pixels package main diff --git a/examples/shader/radialblur.go b/examples/shader/radialblur.go index f6e648e31..89f82cc72 100644 --- a/examples/shader/radialblur.go +++ b/examples/shader/radialblur.go @@ -14,6 +14,8 @@ //go:build ignore +//ebitengine:embeddedshader + //kage:unit pixels package main diff --git a/examples/shader/texel.go b/examples/shader/texel.go index 7e06e0488..eaca326e5 100644 --- a/examples/shader/texel.go +++ b/examples/shader/texel.go @@ -14,6 +14,8 @@ //go:build ignore +//ebitengine:embeddedshader + //kage:unit pixels package main diff --git a/examples/shader/water.go b/examples/shader/water.go index 59685bdd7..564ad43b9 100644 --- a/examples/shader/water.go +++ b/examples/shader/water.go @@ -14,6 +14,8 @@ //go:build ignore +//ebitengine:embeddedshader + //kage:unit pixels package main diff --git a/internal/shaderlister/main.go b/internal/shaderlister/main.go index 02f7079cf..788054199 100644 --- a/internal/shaderlister/main.go +++ b/internal/shaderlister/main.go @@ -94,7 +94,7 @@ func xmain() error { } pkgs, err := packages.Load(&packages.Config{ - Mode: packages.NeedName | packages.NeedImports | packages.NeedDeps | packages.NeedTypes | packages.NeedSyntax | packages.NeedTypesInfo, + Mode: packages.NeedName | packages.NeedImports | packages.NeedDeps | packages.NeedTypes | packages.NeedSyntax | packages.NeedTypesInfo | packages.NeedEmbedFiles, }, flag.Args()...) if err != nil { return err @@ -124,7 +124,11 @@ func xmain() error { } origN := len(shaders) - shaders = appendShaderSources(shaders, pkg) + shaders, err = appendShaderSources(shaders, pkg) + if err != nil { + visitErr = err + return false + } // Add source hashes. for i := range shaders[origN:] { @@ -175,20 +179,26 @@ func isStandardImportPath(path string) bool { return !strings.Contains(head, ".") } -const directive = "ebitengine:shader" +const ( + shaderDirective = "ebitengine:shader" + embeddedShaderDirective = "ebitengine:embeddedshader" +) -var reDirective = regexp.MustCompile(`(?m)^\s*//` + regexp.QuoteMeta(directive)) +var ( + reShaderDirective = regexp.MustCompile(`(?m)^\s*//` + regexp.QuoteMeta(shaderDirective)) + reEmbeddedShaderDirective = regexp.MustCompile(`(?m)^\s*//` + regexp.QuoteMeta(embeddedShaderDirective)) +) func hasShaderDirectiveInComment(commentGroup *ast.CommentGroup) bool { for _, line := range commentGroup.List { - if reDirective.MatchString(line.Text) { + if reShaderDirective.MatchString(line.Text) { return true } } return false } -func appendShaderSources(shaders []Shader, pkg *packages.Package) []Shader { +func appendShaderSources(shaders []Shader, pkg *packages.Package) ([]Shader, error) { topLevelDecls := map[ast.Decl]struct{}{} for _, file := range pkg.Syntax { for _, decl := range file.Decls { @@ -220,7 +230,7 @@ func appendShaderSources(shaders []Shader, pkg *packages.Package) []Shader { if genDecl.Lparen != token.NoPos { if genDecl.Doc != nil && hasShaderDirectiveInComment(genDecl.Doc) { pos := pkg.Fset.Position(genDecl.Doc.Pos()) - slog.Warn(fmt.Sprintf("misplaced %s directive", directive), + slog.Warn(fmt.Sprintf("misplaced %s directive", shaderDirective), "package", pkg.PkgPath, "file", pos.Filename, "line", pos.Line, @@ -268,7 +278,7 @@ func appendShaderSources(shaders []Shader, pkg *packages.Package) []Shader { if !isTopLevelDecl(genDecl) { pos := pkg.Fset.Position(docPos) - slog.Warn(fmt.Sprintf("misplaced %s directive", directive), + slog.Warn(fmt.Sprintf("misplaced %s directive", shaderDirective), "package", pkg.PkgPath, "file", pos.Filename, "line", pos.Line, @@ -279,7 +289,7 @@ func appendShaderSources(shaders []Shader, pkg *packages.Package) []Shader { // Avoid multiple names like `const a, b = "foo", "bar"` to avoid confusions. if len(spec.Names) != 1 { pos := pkg.Fset.Position(docPos) - slog.Warn(fmt.Sprintf("%s cannot apply to multiple declarations", directive), + slog.Warn(fmt.Sprintf("%s cannot apply to multiple declarations", shaderDirective), "package", pkg.PkgPath, "file", pos.Filename, "line", pos.Line, @@ -293,7 +303,7 @@ func appendShaderSources(shaders []Shader, pkg *packages.Package) []Shader { c, ok := def.(*types.Const) if !ok { pos := pkg.Fset.Position(docPos) - slog.Warn(fmt.Sprintf("%s cannot apply to %s", directive, objectTypeString(def)), + slog.Warn(fmt.Sprintf("%s cannot apply to %s", shaderDirective, objectTypeString(def)), "package", pkg.PkgPath, "file", pos.Filename, "line", pos.Line, @@ -305,7 +315,7 @@ func appendShaderSources(shaders []Shader, pkg *packages.Package) []Shader { val := c.Val() if val.Kind() != constant.String { pos := pkg.Fset.Position(docPos) - slog.Warn(fmt.Sprintf("%s cannot apply to const type of %s", directive, val.Kind()), + slog.Warn(fmt.Sprintf("%s cannot apply to const type of %s", shaderDirective, val.Kind()), "package", pkg.PkgPath, "file", pos.Filename, "line", pos.Line, @@ -325,7 +335,22 @@ func appendShaderSources(shaders []Shader, pkg *packages.Package) []Shader { } }) - return shaders + for _, file := range pkg.EmbedFiles { + content, err := os.ReadFile(file) + if err != nil { + return nil, err + } + if !reEmbeddedShaderDirective.Match(content) { + continue + } + shaders = append(shaders, Shader{ + Package: pkg.PkgPath, + File: file, + Source: string(content), + }) + } + + return shaders, nil } func objectTypeString(obj types.Object) string { diff --git a/internal/shaderlister/shaderlister_test.go b/internal/shaderlister/shaderlister_test.go index 67065c134..6550ba24c 100644 --- a/internal/shaderlister/shaderlister_test.go +++ b/internal/shaderlister/shaderlister_test.go @@ -19,6 +19,7 @@ import ( "encoding/json" "fmt" "os/exec" + "regexp" "slices" "strings" "testing" @@ -58,29 +59,47 @@ func TestRun(t *testing.T) { t.Fatal(err) } - slices.SortFunc(shaders, func(s1, s2 shader) int { - return cmp.Compare(s1.Source, s2.Source) + type filteredShader struct { + shader shader + filteredSource string + } + + re := regexp.MustCompile(`shader \d+`) + var filteredShaders []filteredShader + for _, s := range shaders { + m := re.FindAllString(s.Source, 1) + if len(m) != 1 { + t.Fatalf("invalid source: %q", s.Source) + } + filteredShaders = append(filteredShaders, filteredShader{ + shader: s, + filteredSource: m[0], + }) + } + + slices.SortFunc(filteredShaders, func(s1, s2 filteredShader) int { + return cmp.Compare(s1.filteredSource, s2.filteredSource) }) - if got, want := len(shaders), 6; got != want { + if got, want := len(filteredShaders), 9; got != want { t.Fatalf("len(shaders): got: %d, want: %d", got, want) } - for i, s := range shaders { - if s.Package == "" { + for i, s := range filteredShaders { + if s.shader.Package == "" { t.Errorf("s.Package is empty: %v", s) } - if s.File == "" { + if s.shader.File == "" { t.Errorf("s.File is empty: %v", s) } - hash, err := graphics.CalcSourceHash([]byte(s.Source)) + hash, err := graphics.CalcSourceHash([]byte(s.shader.Source)) if err != nil { t.Fatal(err) } - if got, want := s.SourceHash, hash.String(); got != want { + if got, want := s.shader.SourceHash, hash.String(); got != want { t.Errorf("s.SourceHash: got: %q, want: %q", got, want) } - if got, want := s.Source, fmt.Sprintf("shader %d", i+1); got != want { + if got, want := s.filteredSource, fmt.Sprintf("shader %d", i+1); got != want { t.Errorf("s.Source: got: %q, want: %q", got, want) } } diff --git a/internal/shaderlister/shaderlistertest/def.go b/internal/shaderlister/shaderlistertest/def.go index baa78b148..118c85d5d 100644 --- a/internal/shaderlister/shaderlistertest/def.go +++ b/internal/shaderlister/shaderlistertest/def.go @@ -14,7 +14,11 @@ package shaderlistertest -import "github.com/hajimehoshi/ebiten/v2/internal/shaderlister/shaderlistertest2" +import ( + "embed" + + "github.com/hajimehoshi/ebiten/v2/internal/shaderlister/shaderlistertest2" +) //ebitengine:shader const _ = "shader 1" @@ -66,3 +70,9 @@ const ( //ebitengine:shader _, _ = "ignored", "ignored again" // multiple consts are ignored to avoid confusion. ) + +//go:embed *kage.go +var embed_go []byte + +//go:embed resource +var embed2_go embed.FS diff --git a/internal/shaderlister/shaderlistertest/embed2_kage.go b/internal/shaderlister/shaderlistertest/embed2_kage.go new file mode 100644 index 000000000..1bfd3ed9c --- /dev/null +++ b/internal/shaderlister/shaderlistertest/embed2_kage.go @@ -0,0 +1,21 @@ +// Copyright 2025 The Ebitengine Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build ignore + +//ebitengine:embeddedshader + +package main + +// shader 8 diff --git a/internal/shaderlister/shaderlistertest/embed_kage.go b/internal/shaderlister/shaderlistertest/embed_kage.go new file mode 100644 index 000000000..3a3a1b439 --- /dev/null +++ b/internal/shaderlister/shaderlistertest/embed_kage.go @@ -0,0 +1,21 @@ +// Copyright 2025 The Ebitengine Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build ignore + +//ebitengine:embeddedshader + +package main + +// shader 7 diff --git a/internal/shaderlister/shaderlistertest/embed_notkage.go b/internal/shaderlister/shaderlistertest/embed_notkage.go new file mode 100644 index 000000000..66bbc9971 --- /dev/null +++ b/internal/shaderlister/shaderlistertest/embed_notkage.go @@ -0,0 +1,21 @@ +// Copyright 2025 The Ebitengine Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build ignore + +// This file lacks the directive `ebitengine:embeddedshader`, so this is not considered as a shader file by shaderlister. + +package main + +// shader 0 diff --git a/internal/shaderlister/shaderlistertest/resource/embed_kage.go b/internal/shaderlister/shaderlistertest/resource/embed_kage.go new file mode 100644 index 000000000..46bc1d959 --- /dev/null +++ b/internal/shaderlister/shaderlistertest/resource/embed_kage.go @@ -0,0 +1,21 @@ +// Copyright 2025 The Ebitengine Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build ignore + +//ebitengine:embeddedshader + +package main + +// shader 9 diff --git a/internal/shaderlister/shaderlistertest/resource/embed_notkage.go b/internal/shaderlister/shaderlistertest/resource/embed_notkage.go new file mode 100644 index 000000000..66bbc9971 --- /dev/null +++ b/internal/shaderlister/shaderlistertest/resource/embed_notkage.go @@ -0,0 +1,21 @@ +// Copyright 2025 The Ebitengine Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build ignore + +// This file lacks the directive `ebitengine:embeddedshader`, so this is not considered as a shader file by shaderlister. + +package main + +// shader 0