internal/glfw: Add a new build tag ebitenexternaldll to requrie external DLLs

Embedding a DLL file and extrating it on the fly can be problematic.
The application might be unexpectedly recognized as a virus by some
virus checkers.

To mitigate this issue, Ebiten adds a new build mode `ebitenexternaldll`
which prevents Ebiten from embedding DLL files. Instead, the application
will require DLL files for GLFW.

Closes #1832
This commit is contained in:
Hajime Hoshi 2021-10-14 00:29:45 +09:00
parent bce2e83565
commit 8b775cf0dd
11 changed files with 144 additions and 62 deletions

11
doc.go
View File

@ -78,4 +78,15 @@
// to manage threads yourself. Functions like IsKeyPressed will no longer be concurrent-safe with this build tag.
// They must be called from the main thread or the same goroutine as the given game's callback functions like Update
// to RunGame.
//
// `ebitenexternaldll` stops embedding DLL file in a Windows executable.
// `ebitenexternaldll` works only for Windows.
// The executable will require a DLL file at the working directory. Copy them from Ebiten repository's `internal/glfw`:
//
// * `glfw_windows_386.dll` for Windows 386
// * `glfw_windows_amd64.dll` for Windows amd64
//
// Embedding a DLL and extracting it on the fly might be problematic on Windows since the application might be
// unexpectedly recognized as a virus by some virus checkers.
// `ebitenexternaldll` is useful for such cases. See #1832 for the discussion.
package ebiten

View File

@ -0,0 +1,36 @@
// Copyright 2021 The Ebiten 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 ebitenexternaldll
// +build ebitenexternaldll
package glfw
import (
"fmt"
"runtime"
"golang.org/x/sys/windows"
)
func loadDLL() (*dll, error) {
name := "glfw_windows_" + runtime.GOARCH + ".dll"
d := windows.NewLazyDLL(name)
if err := d.Load(); err != nil {
return nil, fmt.Errorf("glfw: failed to load %s: %w", name, err)
}
return &dll{
d: d,
}, nil
}

View File

@ -127,7 +127,7 @@ func run() error {
defer out.Close()
// As the file name already specified the build environment, buildtags are not requried.
if err := file2byteslice.Write(out, in, true, "", "glfw", "glfwDLLCompressed"); err != nil {
if err := file2byteslice.Write(out, in, true, "!ebitenexternaldll", "glfw", "glfwDLLCompressed"); err != nil {
return err
}
@ -141,6 +141,9 @@ func run() error {
hashsum := hash.Sum(nil)
if _, err := fmt.Fprintf(hashout, `// Code generated by gen.go. DO NOT EDIT.
//go:build !ebitenexternaldll
// +build !ebitenexternaldll
package glfw
const glfwDLLHash = "%s"
@ -154,9 +157,6 @@ const glfwDLLHash = "%s"
return err
}
}
if err := os.Remove(dll); err != nil {
return err
}
}
if err := execCommand("gofmt", "-s", "-w", "."); err != nil {

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,5 +1,8 @@
// Code generated by gen.go. DO NOT EDIT.
//go:build !ebitenexternaldll
// +build !ebitenexternaldll
package glfw
const glfwDLLHash = "9b66cd40f92263367f0076b690cb90174cc981f0c126eb395bc4e35cd8cb767d"

View File

@ -1,5 +1,8 @@
// Code generated by gen.go. DO NOT EDIT.
//go:build !ebitenexternaldll
// +build !ebitenexternaldll
package glfw
const glfwDLLHash = "654e301e14362491530e1c9e2dde434d96eb5c6b11de42af62d85d9cc170f4d8"

View File

@ -15,12 +15,7 @@
package glfw
import (
"bytes"
"compress/gzip"
"fmt"
"io"
"os"
"path/filepath"
"unsafe"
"golang.org/x/sys/windows"
@ -43,59 +38,6 @@ func (d *dll) call(name string, args ...uintptr) uintptr {
return r
}
func writeDLLFile(name string) error {
f, err := gzip.NewReader(bytes.NewReader(glfwDLLCompressed))
if err != nil {
return err
}
defer f.Close()
out, err := os.Create(name)
if err != nil {
return err
}
defer out.Close()
if _, err := io.Copy(out, f); err != nil {
return err
}
return nil
}
func loadDLL() (*dll, error) {
cachedir, err := os.UserCacheDir()
if err != nil {
return nil, err
}
dir := filepath.Join(cachedir, "ebiten")
if err := os.MkdirAll(dir, 0755); err != nil {
return nil, err
}
fn := filepath.Join(dir, glfwDLLHash+".dll")
if _, err := os.Stat(fn); err != nil {
if !os.IsNotExist(err) {
return nil, err
}
// Create a DLL as a temporary file and then rename it later.
// Without the temporary file, writing a DLL might fail in the process of writing and Ebiten cannot
// notice that the DLL file is incomplete.
if err := writeDLLFile(fn + ".tmp"); err != nil {
return nil, err
}
if err := os.Rename(fn+".tmp", fn); err != nil {
return nil, err
}
}
return &dll{
d: windows.NewLazyDLL(fn),
}, nil
}
func (d *dll) unload() error {
if err := windows.FreeLibrary(windows.Handle(d.d.Handle())); err != nil {
return err

View File

@ -0,0 +1,81 @@
// Copyright 2021 The Ebiten 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 !ebitenexternaldll
// +build !ebitenexternaldll
package glfw
import (
"bytes"
"compress/gzip"
"io"
"os"
"path/filepath"
"golang.org/x/sys/windows"
)
func writeDLLFile(name string) error {
f, err := gzip.NewReader(bytes.NewReader(glfwDLLCompressed))
if err != nil {
return err
}
defer f.Close()
out, err := os.Create(name)
if err != nil {
return err
}
defer out.Close()
if _, err := io.Copy(out, f); err != nil {
return err
}
return nil
}
func loadDLL() (*dll, error) {
cachedir, err := os.UserCacheDir()
if err != nil {
return nil, err
}
dir := filepath.Join(cachedir, "ebiten")
if err := os.MkdirAll(dir, 0755); err != nil {
return nil, err
}
fn := filepath.Join(dir, glfwDLLHash+".dll")
if _, err := os.Stat(fn); err != nil {
if !os.IsNotExist(err) {
return nil, err
}
// Create a DLL as a temporary file and then rename it later.
// Without the temporary file, writing a DLL might fail in the process of writing and Ebiten cannot
// notice that the DLL file is incomplete.
if err := writeDLLFile(fn + ".tmp"); err != nil {
return nil, err
}
if err := os.Rename(fn+".tmp", fn); err != nil {
return nil, err
}
}
return &dll{
d: windows.NewLazyDLL(fn),
}, nil
}