Compare commits

..

2 Commits

Author SHA1 Message Date
Lea Anthony
f63df796fd Merge pull request #90 from fishfishfish2104/v0.14.0-pre
Fixed Manjaro not being detected as arch
2019-05-17 12:46:57 +10:00
fred2104
cd370f0a39 Fixed Manjaro not being detected as arch 2019-05-17 13:40:34 +12:00
1052 changed files with 14110 additions and 132575 deletions

42
.chglog/CHANGELOG.tpl.md Executable file
View File

@@ -0,0 +1,42 @@
{{ if .Versions -}}
<a name="unreleased"></a>
## [Unreleased]
{{ if .Unreleased.CommitGroups -}}
{{ range .Unreleased.CommitGroups -}}
### {{ .Title }}
{{ range .Commits -}}
- {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }}
{{ end }}
{{ end -}}
{{ end -}}
{{ end -}}
{{ range .Versions }}
<a name="{{ .Tag.Name }}"></a>
## {{ if .Tag.Previous }}[{{ .Tag.Name }}]{{ else }}{{ .Tag.Name }}{{ end }} - {{ datetime "2006-01-02" .Tag.Date }}
{{ range .CommitGroups -}}
### {{ .Title }}
{{ range .Commits -}}
- {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }}
{{ end }}
{{ end -}}
{{- if .NoteGroups -}}
{{ range .NoteGroups -}}
### {{ .Title }}
{{ range .Notes }}
{{ .Body }}
{{ end }}
{{ end -}}
{{ end -}}
{{ end -}}
{{- if .Versions }}
[Unreleased]: {{ .Info.RepositoryURL }}/compare/{{ $latest := index .Versions 0 }}{{ $latest.Tag.Name }}...HEAD
{{ range .Versions -}}
{{ if .Tag.Previous -}}
[{{ .Tag.Name }}]: {{ $.Info.RepositoryURL }}/compare/{{ .Tag.Previous.Name }}...{{ .Tag.Name }}
{{ end -}}
{{ end -}}
{{ end -}}

27
.chglog/config.yml Executable file
View File

@@ -0,0 +1,27 @@
style: github
template: CHANGELOG.tpl.md
info:
title: CHANGELOG
repository_url: https://github.com/wailsapp/wails
options:
commits:
# filters:
# Type:
# - feat
# - fix
# - perf
# - refactor
commit_groups:
# title_maps:
# feat: Features
# fix: Bug Fixes
# perf: Performance Improvements
# refactor: Code Refactoring
header:
pattern: "^(\\w*)\\:\\s(.*)$"
pattern_maps:
- Type
- Subject
notes:
keywords:
- BREAKING CHANGE

View File

@@ -1 +0,0 @@
runtime/assets/default.html

View File

@@ -1,30 +0,0 @@
{
"env": {
"browser": true,
"es6": true,
"node": true,
},
"extends": "eslint:recommended",
"parserOptions": {
"ecmaVersion": 2016,
"sourceType": "module",
},
"rules": {
"indent": [
"error",
"tab"
],
"linebreak-style": [
"error",
"unix"
],
"quotes": [
"error",
"single"
],
"semi": [
"error",
"always"
]
}
}

12
.github/FUNDING.yml vendored
View File

@@ -1,12 +0,0 @@
# These are supported funding model platforms
github: [ leaanthony ]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']

View File

@@ -7,15 +7,6 @@ assignees: ''
---
#####################################################
**V1 users: If you have a technical issue, please do not open a bug this way!**
Please use the `wails issue` command!
If you do not do this then the issue may be closed automatically.
NOTE: If your bug is related to Windows, make sure you read
the [Windows Developer Guide](https://wails.app/guides/windows/)
#####################################################
**Description**
A clear and concise description of what the bug is.
@@ -33,9 +24,7 @@ A clear and concise description of what you expected to happen.
If applicable, add screenshots to help explain your problem.
**System Details**
V2 users: Please add the output of `wails doctor`.
Please paste the output of `wails report` here.
**Additional context**
Add any other context about the problem here.
- [ ] This issue is for Windows and I have read the [Windows Developer Guide](https://wails.app/guides/windows/)

View File

@@ -1,20 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

19
.github/stale.yml vendored
View File

@@ -1,19 +0,0 @@
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 30
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 7
# Issues with these labels will never be considered stale
exemptLabels:
- pinned
- security
- onhold
- inprogress
# Label to use when marking an issue as stale
staleLabel: wontfix
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false

View File

@@ -1,35 +0,0 @@
name: Deploy mirror | 部署镜像
on:
push:
branches: [master]
# pull_request:
# branches: [ main ]
jobs:
build-and-deploy:
name: Automatic deployment | 自动部署
runs-on: ubuntu-latest
if: github.repository == 'misitebao/wails'
steps:
- name: Checkout | 切换到部署分支
uses: actions/checkout@v2
with:
ref: "master"
submodules: true
fetch-depth: 0
- name: Build Site | 构建网站
run: |
cd website &&
npm install && npm run build
- name: Deploy to Server | 部署到服务器
uses: hengkx/ssh-deploy@v1.0.1
with:
HOST: ${{ secrets.DEPLOY_HOST }}
USERNAME: ${{ secrets.DEPLOY_HOST_USER }}
PASSWORD: ${{ secrets.DEPLOY_HOST_PASSWORD }}
SOURCE: "website/build"
TARGET: "/www/wwwroot/wails.top"

View File

@@ -1,34 +0,0 @@
name: latest pre-release
on:
push:
branches:
- develop
tags:
- '**-pre**'
jobs:
build:
name: Test Build Latest Pre-Release
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macOS-latest]
steps:
- name: Set up Go 1.16
uses: actions/setup-go@v1
with:
go-version: 1.16
id: go
- name: Check out code into the Go module directory
uses: actions/checkout@v1
- name: Get dependencies
run: |
go get -v -d ./...
- name: Build
run: go build -v ./cmd/wails
- name: Test
run: ./wails version

View File

@@ -1,32 +0,0 @@
name: pr
on:
pull_request:
branches:
- develop
jobs:
build:
name: Test Build PR
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macOS-latest]
steps:
- name: Set up Go 1.16
uses: actions/setup-go@v1
with:
go-version: 1.16
id: go
- name: Check out code into the Go module directory
uses: actions/checkout@v1
- name: Get dependencies
run: |
go get -v -d ./...
- name: Build
run: go build -v ./cmd/wails
- name: Test
run: ./wails version

View File

@@ -1,34 +0,0 @@
name: release
on:
push:
branches:
- master
tags:
- '!**pre**'
jobs:
build:
name: Test Build Latest Release
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macOS-latest]
steps:
- name: Set up Go 1.16
uses: actions/setup-go@v1
with:
go-version: 1.16
id: go
- name: Check out code into the Go module directory
uses: actions/checkout@v1
- name: Get dependencies
run: |
go get -v -d ./...
- name: Build
run: go build -v ./cmd/wails
- name: Test
run: ./wails version

View File

@@ -1,29 +0,0 @@
name: Runtime
on:
push:
branches:
- v2-alpha
paths:
- 'v2/internal/frontend/runtime/**'
jobs:
rebuild-runtime:
name: Rebuild the runtime
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: 14.17.6
cache: 'npm'
cache-dependency-path: v2/internal/frontend/runtime/package-lock.json
- run: npm install
working-directory: v2/internal/frontend/runtime
- run: npm run build
working-directory: v2/internal/frontend/runtime
- name: Commit changes
uses: devops-infra/action-commit-push@master
with:
github_token: "${{ secrets.GITHUB_TOKEN }}"
commit_prefix: "[AUTO]"
commit_message: "The runtime was rebuilt"

16
.gitignore vendored
View File

@@ -16,18 +16,4 @@ examples/**/example*
cmd/wails/wails
.DS_Store
tmp
node_modules/
package.json.md5
v2/test/**/frontend/dist
v2/test/**/build/
v2/test/frameless/icon.png
v2/test/hidden/icon.png
v2/test/kitchensink/frontend/public/bundle.*
v2/pkg/parser/testproject/frontend/wails
v2/test/kitchensink/frontend/public
v2/test/kitchensink/build/darwin/desktop/kitchensink
v2/test/kitchensink/frontend/package.json.md5
/v2/internal/ffenestri/windows/test/cmake-build-debug/
!v2/internal/ffenestri/windows/x64/webview2.dll
!v2/internal/ffenestri/windows/x64/WebView2Loader.dll
.idea/
dist

View File

@@ -1,6 +0,0 @@
jshint:
config_file: .jshintrc
eslint:
enabled: true
config_file: .eslintrc
ignore_file: .eslintignore

View File

@@ -1,3 +0,0 @@
{
"esversion": 6
}

30
.vscode/launch.json vendored
View File

@@ -5,40 +5,12 @@
"version": "0.2.0",
"configurations": [
{
"name": "Test cmd package",
"type": "go",
"request": "launch",
"mode": "test",
"program": "${workspaceFolder}/cmd/"
},
{
"name": "Wails Init",
"name": "Launch",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${workspaceFolder}/cmd/wails/main.go",
"env": {},
"cwd": "/tmp",
"args": [
"init",
"-name",
"runtime",
"-dir",
"runtime",
"-output",
"runtime",
"-template",
"vuebasic"
]
},
{
"name": "Wails Update Pre",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${workspaceFolder}/cmd/wails/main.go",
"env": {},
"cwd": "/tmp",
"args": [
"update",
"-pre"

View File

@@ -1,8 +1,3 @@
{
"go.formatTool": "goimports",
"eslint.alwaysShowStatus": true,
"files.associations": {
"__locale": "c",
"ios": "c"
}
"go.formatTool": "goimports"
}

View File

@@ -1,34 +1,19 @@
2019-07-20 **v0.17.6-pre**
* Significant refactor of runtime
* Removed wailsbridge file - now a unified approach taken
* Fixed React on Windows - Thanks [Florian Didran](https://github.com/fdidron)!
<a name="v0.13.0"></a>
## [v0.13.0] - 2019-05-12
2019-06-18 **v0.16.0**
* React template FTW! - Thanks [admin_3.exe](https://github.com/bh90210)!
* Updated contributors
* Arch Linux detection without lsb-release
* Removed deprecated methods for dealing with JS/CSS in the backend
### Feat
- revamped 'update' system
- no need for explicit GO111MODULE=on
2019-05-29 **v0.14.11-pre**
* Windows fix for spinner
### Fix
- documentation typo fixes
- windows init project
- windows 10 colour
- leave windows assets on -p flag
- show prerequisite errors
2019-05-29 **v0.14.10-pre**
* Windows fix for Vuetify
2019-05-29 **v0.14.9-pre**
* Vuetify project template 🎉
2019-05-29 **v0.14.8-pre**
* Updated Ubuntu npm install command
2019-05-22 **v0.14.7-pre**
* New projects are built automatically when initialised
* Go 1.12 is now a minimum requirement
2019-05-21 **v0.14.6-pre**
* Hotfix for module dependency issue
2019-05-20 **v0.14.5-pre**
* Added developer tooling - New Template Generator
* Documentation fixes - Thanks [admin_3.exe](https://github.com/bh90210)!
### Docs
- updated contributors
- added awesomego logo
- added Redhat distro

View File

@@ -2,47 +2,11 @@
Wails is what it is because of the time and effort given by these great people. A huge thank you to each and every one!
* [Dustin Krysak](https://wiki.ubuntu.com/bashfulrobot)
* [Qais Patankar](https://github.com/qaisjp)
* [Anthony Lee](https://github.com/alee792)
* [Adrian Lanzafame](https://github.com/lanzafame)
* [Mattn](https://github.com/mattn)
* [0xflotus](https://github.com/0xflotus)
* [Michael D Henderson](https://github.com/mdhender)
* [fred2104](https://github.com/fishfishfish2104)
* [intelwalk](https://github.com/intelwalk)
* [Mark Stenglein](https://github.com/ocelotsloth)
* [admin_3.exe](https://github.com/bh90210)
* [iceleo-com](https://github.com/iceleo-com)
* [fallendusk](https://github.com/fallendusk)
* [Nikolai Zimmermann](https://github.com/Chronophylos)
* [Toyam Cox](https://github.com/Vaelatern)
* [Robin Eklind](https://github.com/mewmew)
* [Kris Raney](https://github.com/kraney)
* [Jack Mordaunt](https://github.com/JackMordaunt)
* [Michael Hipp](https://github.com/MichaelHipp)
* [Travis McLane](https://github.com/tmclane)
* [Reuben Thomas-Davis](https://github.com/Rested)
* [Jarek](https://github.com/Jarek-SRT)
* [Konez2k](https://github.com/konez2k)
* [msms](https://github.com/sayuthisobri)
* [dedo1911](https://github.com/dedo1911)
* [Florian Didron](https://github.com/fdidron)
* [Christopher Murphy](https://github.com/Splode)
* [Zámbó, Levente](https://github.com/Lyimmi)
* [artem](https://github.com/Unix4ever)
* [Tim Kipp](https://github.com/timkippdev)
* [Dmitry Gomzyakov](https://github.com/kyoto44)
* [Arthur Wiebe](https://github.com/artooro)
* [Ilgıt Yıldırım](https://github.com/ilgityildirim)
* [Altynbek](https://github.com/gelleson)
* [Kyle](https://github.com/kmuchmore)
* [Balakrishna Prasad Ganne](https://github.com/aayush420)
* [Charaf Rezrazi](https://github.com/Rezrazi)
* [misitebao](https://github.com/misitebao)
* [Elie Grenon](https://github.com/DrunkenPoney)
* [SophieAu](https://github.com/SophieAu)
* [Alexander Matviychuk](https://github.com/alexmat)
* [RH12503](https://github.com/RH12503)
* [hi019](https://github.com/hi019)
* [Igor Minen](https://github.com/Igogrek)
* [Dustin Krysak](https://wiki.ubuntu.com/bashfulrobot)
* [Qais Patankar](https://github.com/qaisjp)
* [Anthony Lee](https://github.com/alee792)
* [Adrian Lanzafame](https://github.com/lanzafame)
* [0xflotus](https://github.com/0xflotus)
* [Michael D Henderson](https://github.com/mdhender)
* [fred2104] (https://github.com/fishfishfish2104)
* [intelwalk] (https://github.com/intelwalk)

411
README.md
View File

@@ -1,98 +1,23 @@
<p align="center" style="text-align: center">
<img src="logo.png" width="55%"><br/>
<img src="https://github.com/wailsapp/docs/raw/master/.vuepress/public/media/logo_cropped.png" width="40%"><br/>
</p>
<p align="center">
Build desktop applications using Go & Web Technologies.<br/><br/>
<a href="https://github.com/wailsapp/wails/blob/master/LICENSE">
<img src="https://img.shields.io/badge/License-MIT-blue.svg">
</a>
<a href="https://goreportcard.com/report/github.com/wailsapp/wails">
<img src="https://goreportcard.com/badge/github.com/wailsapp/wails"/>
</a>
<a href="http://godoc.org/github.com/wailsapp/wails">
<img src="https://img.shields.io/badge/godoc-reference-blue.svg"/>
</a>
<a href="https://www.codefactor.io/repository/github/wailsapp/wails">
<img src="https://www.codefactor.io/repository/github/wailsapp/wails/badge" alt="CodeFactor" />
</a>
<a href="https://github.com/wailsapp/wails/issues">
<img src="https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat" alt="CodeFactor" />
</a>
<a href="https://app.fossa.io/projects/git%2Bgithub.com%2Fwailsapp%2Fwails?ref=badge_shield" alt="FOSSA Status">
<img src="https://app.fossa.io/api/projects/git%2Bgithub.com%2Fwailsapp%2Fwails.svg?type=shield"/>
</a>
<a href="https://houndci.com">
<img src="https://img.shields.io/badge/Reviewed_by-Hound-8E64B0.svg"/>
</a>
<a href="https://github.com/avelino/awesome-go" rel="nofollow">
<img src="https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg" alt="Awesome"/>
</a>
<a href="https://github.com/wailsapp/wails/workflows/release/badge.svg?branch=master" rel="nofollow">
<img src="https://github.com/wailsapp/wails/workflows/release/badge.svg?branch=master" alt="Release Pipelines"/>
</a>
A framework for building desktop applications using Go & Web Technologies.<br/><br/>
<a href="https://github.com/wailsapp/wails/blob/master/LICENSE"><img src="https://img.shields.io/badge/License-MIT-blue.svg"></a>
<a href="https://goreportcard.com/report/github.com/wailsapp/wails"><img src="https://goreportcard.com/badge/github.com/wailsapp/wails"/></a>
<a href="http://godoc.org/github.com/wailsapp/wails"><img src="https://img.shields.io/badge/godoc-reference-blue.svg"/></a>
<a href="https://www.codefactor.io/repository/github/wailsapp/wails"><img src="https://www.codefactor.io/repository/github/wailsapp/wails/badge" alt="CodeFactor" /></a>
<a href="https://github.com/wailsapp/wails/issues"><img src="https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat" alt="CodeFactor" /></a>
<a href="https://houndci.com"><img src="https://img.shields.io/badge/Reviewed_by-Hound-8E64B0.svg"/></a>
<a href="https://github.com/sindresorhus/awesome" rel="nofollow"><img src="https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg" alt="Awesome"></a>-
</p>
<span id="nav-1"></span>
## Internationalization
[English](README.md) | [简体中文](README.zh-Hans.md)
<span id="nav-2"></span>
## Table of Contents
<details>
<summary>Click me to Open/Close the directory listing</summary>
- [1. Internationalization](#nav-1)
- [2. Table of Contents](#nav-2)
- [3. Introduction](#nav-3)
- [3.1 Official Website](#nav-3-1)
- [4. Features](#nav-4)
- [5. Sponsors](#nav-5)
- [6. Installation](#nav-6)
- [6.1 MacOS](#nav-6-1)
- [6.2 Linux](#nav-6-2)
- [6.2.1 Debian/Ubuntu](#nav-6-2-1)
- [6.2.2 Arch Linux / ArchLabs / Ctlos Linux](#nav-6-2-2)
- [6.2.3 Centos](#nav-6-2-3)
- [6.2.4 Fedora](#nav-6-2-4)
- [6.2.5 VoidLinux & VoidLinux-musl](#nav-6-2-5)
- [6.2.6 Gentoo](#nav-6-2-6)
- [6.3 Windows](#nav-6-3)
- [7. Usage](#nav-7)
- [7.1 Next Steps](#nav-7-1)
- [8. FAQ](#nav-8)
- [9. Contributors](#nav-9)
- [10. Special Mentions](#nav-10)
- [12. Special Thanks](#nav-11)
</details>
<span id="nav-3"></span>
## Introductions
The traditional method of providing web interfaces to Go programs is via a built-in web server. Wails offers a different
approach: it provides the ability to wrap both Go code and a web frontend into a single binary. Tools are provided to
make this easy for you by handling project creation, compilation and bundling. All you have to do is get creative!
<span id="nav-3-1"></span>
### Official Website
The official docs can be found at [https://wails.app](https://wails.app).
Click [here](https://wails.io) if you are interested in trying out v2 Beta for Windows.
<span id="nav-4"></span>
The traditional method of providing web interfaces to Go programs is via a built-in web server. Wails offers a different approach: it provides the ability to wrap both Go code and a web frontend into a single binary. Tools are provided to make this easy for you by handling project creation, compilation and bundling. All you have to do is get creative!
## Features
- Use standard Go for the backend
- Use standard Go libraries/frameworks for the backend
- Use any frontend technology to build your UI
- Quickly create Vue, Vuetify or React frontends for your Go programs
- Expose Go methods/functions to the frontend via a single bind command
- Uses native rendering engines - no embedded browser
- Shared events system
@@ -100,320 +25,96 @@ Click [here](https://wails.io) if you are interested in trying out v2 Beta for W
- Powerful cli tool
- Multiplatform
<span id="nav-5"></span>
## Project Status
## Sponsors
This project is supported by these kind people / companies:
<p align="center">
<a href="https://www.easywebadv.it/" style="width:150px;">
<img src="website/static/img/easyweb.png" width="150"/>
</a>
</p>
<br/>
<br/>
<a href="https://github.com/sponsors/leaanthony" style="width:100px;">
<img src="sponsors/silver%20sponsor.png" width="100"/>
</a>
<a href="https://github.com/letheanVPN" style="width:100px;">
<img src="https://github.com/letheanVPN.png?size=100" width="100"/>
</a>
<br/>
<br/>
<a href="https://github.com/sponsors/leaanthony" style="width:100px;">
<img src="sponsors/bronze%20sponsor.png" width="100"/>
</a>
<a href="https://github.com/snider" style="width:100px;">
<img src="https://github.com/snider.png?size=100" width="100"/>
</a>
<a href="https://github.com/codydbentley" style="width:100px">
<img src="https://github.com/codydbentley.png?size=100" width="100"/>
</a>
<a href="https://github.com/CrackDavid" style="width:100px">
<img src="https://github.com/CrackDavid.png?size=100" width="100"/>
</a>
<br/>
<br/>
<a href="https://github.com/matryer" style="width:100px">
<img src="https://github.com/matryer.png" width="100"/>
</a>
<a href="https://www.jetbrains.com?from=Wails" style="width:100px">
<img src="/assets/images/jetbrains-grayscale.png" width="100"/>
</a>
<a href="https://github.com/tc-hib" style="width:55px">
<img src="https://github.com/tc-hib.png?size=55" width="55"/>
</a>
<a href="https://github.com/picatz" style="width:50px">
<img src="https://github.com/picatz.png?size=50" width="50"/>
</a>
<a href="https://github.com/tylertravisty" style="width:50px">
<img src="https://github.com/tylertravisty.png?size=50" width="50"/>
</a>
<a href="https://github.com/akhudek" style="width:50px">
<img src="https://github.com/akhudek.png?size=50" width="50"/>
</a>
<a href="https://github.com/trea" style="width:50px">
<img src="https://github.com/trea.png?size=50" width="50"/>
</a>
<a href="https://github.com/LanguageAgnostic" style="width:55px">
<img src="https://github.com/LanguageAgnostic.png?size=55" width="55"/>
</a>
<a href="https://github.com/fcjr" style="width:55px">
<img src="https://github.com/fcjr.png?size=55" width="55"/>
</a>
<a href="https://github.com/nickarellano" style="width:60px">
<img src="https://github.com/nickarellano.png?size=60" width="60"/>
</a>
<a href="https://github.com/bglw" style="width:65px">
<img src="https://github.com/bglw.png?size=65" width="65"/>
</a>
<a href="https://github.com/jugglingjsons" style="width:50px">
<img src="https://github.com/jugglingjsons.png?size=50" width="50"/>
</a>
<a href="https://github.com/marcus-crane" style="width:65px">
<img src="https://github.com/marcus-crane.png?size=65" width="65"/>
</a>
<a href="https://github.com/bbergshaven" style="width:45px">
<img src="https://github.com/bbergshaven.png?size=45" width="45"/>
</a>
<a href="https://github.com/Gilgames000" style="width:45px">
<img src="https://github.com/Gilgames000.png?size=45" width="45"/>
</a>
<a href="https://github.com/ilgityildirim" style="width:50px">
<img src="https://github.com/ilgityildirim.png?size=50" width="50"/>
</a>
<a href="https://github.com/questrail" style="width:50px">
<img src="https://github.com/questrail.png?size=50" width="50"/>
</a>
<a href="https://github.com/DonTomato" style="width:45px">
<img src="https://github.com/DonTomato.png?size=45" width="45"/>
</a>
<a href="https://github.com/taigrr" style="width:45px">
<img src="https://github.com/taigrr.png?size=45" width="45"/>
</a>
<a href="https://github.com/charlie-dee" style="width:55px">
<img src="https://github.com/charlie-dee.png?size=55" width="55"/>
</a>
<a href="https://github.com/EdenNetworkItalia" style="width:65px">
<img src="https://github.com/EdenNetworkItalia.png?size=65" width="65"/>
</a>
<a href="https://github.com/michaelolson1996" style="width:55px">
<img src="https://github.com/michaelolson1996.png?size=55" width="55"/>
</a>
<a href="https://github.com/GargantuaX" style="width:45px">
<img src="https://github.com/GargantuaX.png?size=45" width="45"/>
</a>
<span id="nav-6"></span>
Wails is currently in Beta. Please make sure you read the [Project Status](https://wails.app/project_status.html) if you are interested in using this project.
## Installation
Wails uses cgo to bind to the native rendering engines so a number of platform dependent libraries are needed as well as
an installation of Go. The basic requirements are:
Wails uses cgo to bind to the native rendering engines so a number of platform dependent libraries are needed as well as an installation of Go. The basic requirements are:
- Go 1.16
- Go 1.11 or above
- npm
<span id="nav-6-1"></span>
### MacOS
Make sure you have the xcode command line tools installed. This can be done by running:
`xcode-select --install`
<span id="nav-6-2"></span>
### Linux
<span id="nav-6-2-1"></span>
#### Ubuntu 18.04
#### Debian/Ubuntu
`sudo apt install pkg-config build-essential libgtk-3-dev libwebkit2gtk-4.0-dev`
`sudo apt install libgtk-3-dev libwebkit2gtk-4.0-dev`
_Debian: 8, 9, 10_
_Ubuntu: 16.04, 18.04, 19.04_
_Also succesfully tested on: Zorin 15, Parrot 4.7, Linuxmint 19, Elementary 5, Kali, Neon_, Pop!\_OS
<span id="nav-6-2-2"></span>
#### Arch Linux / ArchLabs / Ctlos Linux
#### Arch Linux
`sudo pacman -S webkit2gtk gtk3`
_Also succesfully test on: Manjaro & ArcoLinux_
#### Red Hat Based Distros
<span id="nav-6-2-3"></span>
`sudo yum install webkit2gtk-devel gtk3-devel`
#### Centos
`sudo yum install webkitgtk3-devel gtk3-devel`
_CentOS 6, 7_
<span id="nav-6-2-4"></span>
#### Fedora
`sudo yum install webkit2gtk3-devel gtk3-devel`
_Fedora 29, 30_
<span id="nav-6-2-5"></span>
#### VoidLinux & VoidLinux-musl
`xbps-install gtk+3-devel webkit2gtk-devel`
<span id="nav-6-2-6"></span>
#### Gentoo
`sudo emerge gtk+:3 webkit-gtk`
<span id="nav-6-3"></span>
Note: If you have successfully installed these dependencies on a different flavour of Linux, please consider submitting a PR.
### Windows
Windows requires gcc and related tooling. The recommended download is
from [http://tdm-gcc.tdragon.net/download](http://tdm-gcc.tdragon.net/download). Once this is installed, you are good to
go.
Windows requires gcc and related tooling. The recommended download is from [http://tdm-gcc.tdragon.net/download](http://tdm-gcc.tdragon.net/download). Once this is installed, you are good to go.
<span id="nav-7"></span>
## Usage
**Ensure Go modules are enabled: GO111MODULE=on and go/bin is in your PATH variable.**
## Installation
Installation is as simple as running the following command:
```
go get -u github.com/wailsapp/wails/cmd/wails
```
<pre style='color:white'>
go get github.com/wailsapp/wails/cmd/wails
</pre>
<span id="nav-7-1"></span>
### Next Steps
## Next Steps
It is recommended at this stage to read the comprehensive documentation at [https://wails.app](https://wails.app).
<span id="nav-8"></span>
## FAQ
- Is this an alternative to Electron?
* Is this an alternative to Electron?
Depends on your requirements. It's designed to make it easy for Go programmers to make lightweight desktop
applications or add a frontend to their existing applications. Whilst Wails does not currently offer hooks into native
elements such as menus, this may change in the future.
Depends on your requirements. It's designed to make it easy for Go programmers to make lightweight desktop applications or add a frontend to their existing applications. Whilst Wails does not currently offer hooks into native elements such as menus, this may change in the future.
- Who is this project aimed at?
* Who is this project aimed at?
Go programmers who want to bundle an HTML/JS/CSS frontend with their applications, without resorting to creating a
server and opening a browser to view it.
Go programmers who want to bundle an HTML/JS/CSS frontend with their applications, without resorting to creating a server and opening a browser to view it.
- What's with the name?
* What's with the name?
When I saw WebView, I thought "What I really want is tooling around building a WebView app, a bit like Rails is to
Ruby". So initially it was a play on words (Webview on Rails). It just so happened to also be a homophone of the
English name for the [Country](https://en.wikipedia.org/wiki/Wales) I am from. So it stuck.
When I saw WebView, I thought "What I really want is tooling around building a WebView app, a bit like Rails is to Ruby". So initially it was a play on words (Webview on Rails). It just so happened to also be a homophone of the English name for the [Country](https://en.wikipedia.org/wiki/Wales) I am from. So it stuck.
<span id="nav-9"></span>
## Contributors
<a href="https://github.com/qaisjp"><img src="https://github.com/qaisjp.png?size=40" width="40"/></a>
<a href="https://github.com/alee792"><img src="https://github.com/alee792.png?size=40" width="40"/></a>
<a href="https://github.com/lanzafame"><img src="https://github.com/lanzafame.png?size=40" width="40"/></a>
<a href="https://github.com/mattn"><img src="https://github.com/mattn.png?size=40" width="40"/></a>
<a href="https://github.com/0xflotus"><img src="https://github.com/0xflotus.png?size=40" width="40"/></a>
<a href="https://github.com/mdhender"><img src="https://github.com/mdhender.png?size=40" width="40"/></a>
<a href="https://github.com/fishfishfish2104"><img src="https://github.com/fishfishfish2104.png?size=40" width="40"/></a>
<a href="https://github.com/intelwalk"><img src="https://github.com/intelwalk.png?size=40" width="40"/></a>
<a href="https://github.com/ocelotsloth"><img src="https://github.com/ocelotsloth.png?size=40" width="40"/></a>
<a href="https://github.com/bh90210"><img src="https://github.com/bh90210.png?size=40" width="40"/></a>
<a href="https://github.com/iceleo-com"><img src="https://github.com/iceleo-com.png?size=40" width="40"/></a>
<a href="https://github.com/fallendusk"><img src="https://github.com/fallendusk.png?size=40" width="40"/></a>
<a href="https://github.com/Chronophylos"><img src="https://github.com/Chronophylos.png?size=40" width="40"/></a>
<a href="https://github.com/Vaelatern"><img src="https://github.com/Vaelatern.png?size=40" width="40"/></a>
<a href="https://github.com/mewmew"><img src="https://github.com/mewmew.png?size=40" width="40"/></a>
<a href="https://github.com/kraney"><img src="https://github.com/kraney.png?size=40" width="40"/></a>
<a href="https://github.com/JackMordaunt"><img src="https://github.com/JackMordaunt.png?size=40" width="40"/></a>
<a href="https://github.com/MichaelHipp"><img src="https://github.com/MichaelHipp.png?size=40" width="40"/></a>
<a href="https://github.com/tmclane"><img src="https://github.com/tmclane.png?size=40" width="40"/></a>
<a href="https://github.com/Rested"><img src="https://github.com/Rested.png?size=40" width="40"/></a>
<a href="https://github.com/Jarek-SRT"><img src="https://github.com/Jarek-SRT.png?size=40" width="40"/></a>
<a href="https://github.com/konez2k"><img src="https://github.com/konez2k.png?size=40" width="40"/></a>
<a href="https://github.com/sayuthisobri"><img src="https://github.com/sayuthisobri.png?size=40" width="40"/></a>
<a href="https://github.com/dedo1911"><img src="https://github.com/dedo1911.png?size=40" width="40"/></a>
<a href="https://github.com/fdidron"><img src="https://github.com/fdidron.png?size=40" width="40"/></a>
<a href="https://github.com/Splode"><img src="https://github.com/Splode.png?size=40" width="40"/></a>
<a href="https://github.com/Lyimmi"><img src="https://github.com/Lyimmi.png?size=40" width="40"/></a>
<a href="https://github.com/Unix4ever"><img src="https://github.com/Unix4ever.png?size=40" width="40"/></a>
<a href="https://github.com/timkippdev"><img src="https://github.com/timkippdev.png?size=40" width="40"/></a>
<a href="https://github.com/kyoto44"><img src="https://github.com/kyoto44.png?size=40" width="40"/></a>
<a href="https://github.com/artooro"><img src="https://github.com/artooro.png?size=40" width="40"/></a>
<a href="https://github.com/ilgityildirim"><img src="https://github.com/ilgityildirim.png?size=40" width="40"/></a>
<a href="https://github.com/gelleson"><img src="https://github.com/gelleson.png?size=40" width="40"/></a>
<a href="https://github.com/kmuchmore"><img src="https://github.com/kmuchmore.png?size=40" width="40"/></a>
<a href="https://github.com/aayush420"><img src="https://github.com/aayush420.png?size=40" width="40"/></a>
<a href="https://github.com/Rezrazi"><img src="https://github.com/Rezrazi.png?size=40" width="40"/></a>
<a href="https://github.com/misitebao"><img src="https://github.com/misitebao.png?size=40" width="40"/></a>
<a href="https://github.com/DrunkenPoney"><img src="https://github.com/DrunkenPoney.png?size=40" width="40"/></a>
<a href="https://github.com/SophieAu"><img src="https://github.com/SophieAu.png?size=40" width="40"/></a>
<a href="https://github.com/alexmat"><img src="https://github.com/alexmat.png?size=40" width="40"/></a>
<a href="https://github.com/RH12503"><img src="https://github.com/RH12503.png?size=40" width="40"/></a>
<a href="https://github.com/hi019"><img src="https://github.com/hi019.png?size=40" width="40"/></a>
<a href="https://github.com/Igogrek"><img src="https://github.com/Igogrek.png?size=40" width="40"/></a>
<a href="https://github.com/aschey"><img src="https://github.com/aschey.png?size=40" width="40"/></a>
<a href="https://github.com/akhudek"><img src="https://github.com/akhudek.png?size=40" width="40"/></a>
<a href="https://github.com/s12chung"><img src="https://github.com/s12chung.png?size=40" width="40"/></a>
<span id="nav-10"></span>
## Special Mentions
## Shoulders of Giants
Without the following people, this project would never have existed:
- [Dustin Krysak](https://wiki.ubuntu.com/bashfulrobot) - His support and feedback has been immense. More patience than
you can throw a stick at (Not long now Dustin!).
- [Serge Zaitsev](https://github.com/zserge) - Creator of [Webview](https://github.com/zserge/webview) which Wails uses
for the windowing.
- [Byron](https://github.com/bh90210) - At times, Byron has single handedly kept this project alive. Without his
incredible input, we never would have got to v1.
* [Dustin Krysak](https://wiki.ubuntu.com/bashfulrobot) - His support and feedback has been immense. More patience than you can throw a stick at (Not long now Dustin!).
* [Serge Zaitsev](https://github.com/zserge) - Creator of [Webview](https://github.com/zserge/webview) which Wails uses for the windowing.
And without [these people](CONTRIBUTORS.md), it wouldn't be what it is today.
Special Mentions:
* [Bill Kennedy](https://twitter.com/goinggodotnet) - Go guru, encourager and all-round nice guy, whose infectious energy and inspiration powered me on when I had none left.
* [Mark Bates](https://github.com/markbates) - Creator of [Packr](https://github.com/gobuffalo/packr), inspiration for packing strategies which fed into some of the tooling.
This project was mainly coded to the following albums:
- [Manic Street Preachers - Resistance Is Futile](https://open.spotify.com/album/1R2rsEUqXjIvAbzM0yHrxA)
- [Manic Street Preachers - This Is My Truth, Tell Me Yours](https://open.spotify.com/album/4VzCL9kjhgGQeKCiojK1YN)
- [The Midnight - Endless Summer](https://open.spotify.com/album/4Krg8zvprquh7TVn9OxZn8)
- [Gary Newman - Savage (Songs from a Broken World)](https://open.spotify.com/album/3kMfsD07Q32HRWKRrpcexr)
- [Steve Vai - Passion & Warfare](https://open.spotify.com/album/0oL0OhrE2rYVns4IGj8h2m)
- [Ben Howard - Every Kingdom](https://open.spotify.com/album/1nJsbWm3Yy2DW1KIc1OKle)
- [Ben Howard - Noonday Dream](https://open.spotify.com/album/6astw05cTiXEc2OvyByaPs)
- [Adwaith - Melyn](https://open.spotify.com/album/2vBE40Rp60tl7rNqIZjaXM)
- [Gwidaith Hen Fran - Cedors Hen Wrach](https://open.spotify.com/album/3v2hrfNGINPLuDP0YDTOjm)
- [Metallica - Metallica](https://open.spotify.com/album/2Kh43m04B1UkVcpcRa1Zug)
- [Bloc Party - Silent Alarm](https://open.spotify.com/album/6SsIdN05HQg2GwYLfXuzLB)
- [Maxthor - Another World](https://open.spotify.com/album/3tklE2Fgw1hCIUstIwPBJF)
- [Alun Tan Lan - Y Distawrwydd](https://open.spotify.com/album/0c32OywcLpdJCWWMC6vB8v)
* [Manic Street Preachers - Resistance Is Futile](https://open.spotify.com/album/1R2rsEUqXjIvAbzM0yHrxA)
* [Manic Street Preachers - This Is My Truth, Tell Me Yours](https://open.spotify.com/album/4VzCL9kjhgGQeKCiojK1YN)
* [The Midnight - Endless Summer](https://open.spotify.com/album/4Krg8zvprquh7TVn9OxZn8)
* [Gary Newman - Savage (Songs from a Broken World)](https://open.spotify.com/album/3kMfsD07Q32HRWKRrpcexr)
* [Steve Vai - Passion & Warfare](https://open.spotify.com/album/0oL0OhrE2rYVns4IGj8h2m)
* [Ben Howard - Every Kingdom](https://open.spotify.com/album/1nJsbWm3Yy2DW1KIc1OKle)
* [Ben Howard - Noonday Dream](https://open.spotify.com/album/6astw05cTiXEc2OvyByaPs)
* [Adwaith - Melyn](https://open.spotify.com/album/2vBE40Rp60tl7rNqIZjaXM)
* [Gwidaith Hen Fran - Cedors Hen Wrach](https://open.spotify.com/album/3v2hrfNGINPLuDP0YDTOjm)
* [Metallica - Metallica](https://open.spotify.com/album/2Kh43m04B1UkVcpcRa1Zug)
* [Bloc Party - Silent Alarm](https://open.spotify.com/album/6SsIdN05HQg2GwYLfXuzLB)
* [Maxthor - Another World](https://open.spotify.com/album/3tklE2Fgw1hCIUstIwPBJF)
* [Alun Tan Lan - Y Distawrwydd](https://open.spotify.com/album/0c32OywcLpdJCWWMC6vB8v)
<span id="nav-11"></span>
## Special Thanks
<p align="center" style="text-align: center">
<a href="https://pace.dev"><img src="/assets/images/pace.jpeg"/></a><br/>
A <i>huge</i> thanks to <a href="https://pace.dev">Pace</a> for sponsoring the project and helping the efforts to get Wails ported to Apple Silicon!<br/><br/>
If you are looking for a Project Management tool that's powerful but quick and easy to use, check them out!<br/><br/>
</p>
<p align="center" style="text-align: center">
A special thank you to JetBrains for donating licenses to us!<br/><br/>
Please click the logo to let them know your appreciation!<br/><br/>
<a href="https://www.jetbrains.com?from=Wails"><img src="/assets/images/jetbrains-grayscale.png" width="30%"></a>
</p>

View File

@@ -1,412 +0,0 @@
<p align="center" style="text-align: center">
<img src="logo.png" width="55%"><br/>
</p>
<p align="center">
使用 Go 和 Web 技术构建桌面应用程序。<br/><br/>
<a href="https://github.com/wailsapp/wails/blob/master/LICENSE">
<img src="https://img.shields.io/badge/License-MIT-blue.svg">
</a>
<a href="https://goreportcard.com/report/github.com/wailsapp/wails">
<img src="https://goreportcard.com/badge/github.com/wailsapp/wails"/>
</a>
<a href="http://godoc.org/github.com/wailsapp/wails">
<img src="https://img.shields.io/badge/godoc-reference-blue.svg"/>
</a>
<a href="https://www.codefactor.io/repository/github/wailsapp/wails">
<img src="https://www.codefactor.io/repository/github/wailsapp/wails/badge" alt="CodeFactor" />
</a>
<a href="https://github.com/wailsapp/wails/issues">
<img src="https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat" alt="CodeFactor" />
</a>
<a href="https://app.fossa.io/projects/git%2Bgithub.com%2Fwailsapp%2Fwails?ref=badge_shield" alt="FOSSA Status">
<img src="https://app.fossa.io/api/projects/git%2Bgithub.com%2Fwailsapp%2Fwails.svg?type=shield"/>
</a>
<a href="https://houndci.com">
<img src="https://img.shields.io/badge/Reviewed_by-Hound-8E64B0.svg"/>
</a>
<a href="https://github.com/avelino/awesome-go" rel="nofollow">
<img src="https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg" alt="Awesome"/>
</a>
<a href="https://github.com/wailsapp/wails/workflows/release/badge.svg?branch=master" rel="nofollow">
<img src="https://github.com/wailsapp/wails/workflows/release/badge.svg?branch=master" alt="Release Pipelines"/>
</a>
</p>
<span id="nav-1"></span>
## 国际化
[English](README.md) | [简体中文](README.zh-Hans.md)
<span id="nav-2"></span>
## 内容目录
<details>
<summary>点我 打开/关闭 目录列表</summary>
- [1. 国际化](#nav-1)
- [2. 内容目录](#nav-2)
- [3. 项目介绍](#nav-3)
- [3.1 官方网站](#nav-3-1)
- [4. 功能](#nav-4)
- [5. 赞助商](#nav-5)
- [6. 安装](#nav-6)
- [6.1 MacOS](#nav-6-1)
- [6.2 Linux](#nav-6-2)
- [6.2.1 Debian/Ubuntu](#nav-6-2-1)
- [6.2.2 Arch Linux / ArchLabs / Ctlos Linux](#nav-6-2-2)
- [6.2.3 Centos](#nav-6-2-3)
- [6.2.4 Fedora](#nav-6-2-4)
- [6.2.5 VoidLinux & VoidLinux-musl](#nav-6-2-5)
- [6.2.6 Gentoo](#nav-6-2-6)
- [6.3 Windows](#nav-6-3)
- [7. 使用方法](#nav-7)
- [7.1 下一步](#nav-7-1)
- [8. 常见问题](#nav-8)
- [9. 贡献者](#nav-9)
- [10. 特别提及](#nav-10)
- [12. 特别感谢](#nav-11)
</details>
<span id="nav-3"></span>
## 项目介绍
为 Go 程序提供 Web 界面的传统方法是通过内置 Web 服务器。Wails 提供了一种不同的方法:它提供了将 Go 代码和 Web
前端一起打包成单个二进制文件的能力。通过提供的工具,可以很轻松的完成项目的创建、编译和打包。你所要做的就是发挥想象力!
<span id="nav-3-1"></span>
### 官方网站
官方文档可以在 [https://wails.app](https://wails.app) 中找到。
如果您对适用于 Windows 的 v2 测试版感兴趣,可以点击[此处](https://wails.io)查看。
镜像网站:
- [中国大陆镜像站点 - https://wails.top](https://wails.top)
<span id="nav-4"></span>
## 功能
- 后端使用标准 Go
- 使用任意前端技术构建 UI 界面
- 快速为您的 Go 应用生成 Vue、Vuetify、React 前端代码
- 通过简单的绑定命令将 Go 方法暴露到前端
- 使用原生渲染引擎 - 无嵌入式浏览器
- 共享事件系统
- 原生文件系统对话框
- 强大的命令行工具
- 跨多个平台
<span id="nav-5"></span>
## 赞助商
这个项目由以下这些人或者公司支持:
<p align="center">
<a href="https://www.easywebadv.it/" style="width:100px;">
<img src="website/static/img/easyweb.png" width="120"/>
</a>
</p>
<br/>
<br/>
<a href="https://github.com/sponsors/leaanthony" style="width:100px;">
<img src="sponsors/silver%20sponsor.png" width="100"/>
</a>
<a href="https://github.com/letheanVPN" style="width:100px;">
<img src="https://github.com/letheanVPN.png?size=100" width="100"/>
</a>
<br/>
<br/>
<a href="https://github.com/sponsors/leaanthony" style="width:100px;">
<img src="sponsors/bronze%20sponsor.png" width="100"/>
</a>
<a href="https://github.com/snider" style="width:100px;">
<img src="https://github.com/snider.png?size=100" width="100"/>
</a>
<a href="https://github.com/codydbentley" style="width:100px">
<img src="https://github.com/codydbentley.png?size=100" width="100"/>
</a>
<a href="https://github.com/CrackDavid" style="width:100px">
<img src="https://github.com/CrackDavid.png?size=100" width="100"/>
</a>
<br/>
<br/>
<a href="https://github.com/matryer" style="width:100px">
<img src="https://github.com/matryer.png" width="100"/>
</a>
<a href="https://www.jetbrains.com?from=Wails" style="width:100px">
<img src="/assets/images/jetbrains-grayscale.png" width="100"/>
</a>
<a href="https://github.com/tc-hib" style="width:55px">
<img src="https://github.com/tc-hib.png?size=55" width="55"/>
</a>
<a href="https://github.com/picatz" style="width:50px">
<img src="https://github.com/picatz.png?size=50" width="50"/>
</a>
<a href="https://github.com/tylertravisty" style="width:50px">
<img src="https://github.com/tylertravisty.png?size=50" width="50"/>
</a>
<a href="https://github.com/akhudek" style="width:50px">
<img src="https://github.com/akhudek.png?size=50" width="50"/>
</a>
<a href="https://github.com/trea" style="width:50px">
<img src="https://github.com/trea.png?size=50" width="50"/>
</a>
<a href="https://github.com/LanguageAgnostic" style="width:55px">
<img src="https://github.com/LanguageAgnostic.png?size=55" width="55"/>
</a>
<a href="https://github.com/fcjr" style="width:55px">
<img src="https://github.com/fcjr.png?size=55" width="55"/>
</a>
<a href="https://github.com/nickarellano" style="width:60px">
<img src="https://github.com/nickarellano.png?size=60" width="60"/>
</a>
<a href="https://github.com/bglw" style="width:65px">
<img src="https://github.com/bglw.png?size=65" width="65"/>
</a>
<a href="https://github.com/jugglingjsons" style="width:50px">
<img src="https://github.com/jugglingjsons.png?size=50" width="50"/>
</a>
<a href="https://github.com/marcus-crane" style="width:65px">
<img src="https://github.com/marcus-crane.png?size=65" width="65"/>
</a>
<a href="https://github.com/bbergshaven" style="width:45px">
<img src="https://github.com/bbergshaven.png?size=45" width="45"/>
</a>
<a href="https://github.com/Gilgames000" style="width:45px">
<img src="https://github.com/Gilgames000.png?size=45" width="45"/>
</a>
<a href="https://github.com/ilgityildirim" style="width:50px">
<img src="https://github.com/ilgityildirim.png?size=50" width="50"/>
</a>
<a href="https://github.com/questrail" style="width:50px">
<img src="https://github.com/questrail.png?size=50" width="50"/>
</a>
<a href="https://github.com/DonTomato" style="width:45px">
<img src="https://github.com/DonTomato.png?size=45" width="45"/>
</a>
<a href="https://github.com/taigrr" style="width:45px">
<img src="https://github.com/taigrr.png?size=45" width="45"/>
</a>
<a href="https://github.com/charlie-dee" style="width:55px">
<img src="https://github.com/charlie-dee.png?size=55" width="55"/>
</a>
<a href="https://github.com/EdenNetworkItalia" style="width:65px">
<img src="https://github.com/EdenNetworkItalia.png?size=65" width="65"/>
</a>
<a href="https://github.com/michaelolson1996" style="width:55px">
<img src="https://github.com/michaelolson1996.png?size=55" width="55"/>
</a>
<a href="https://github.com/GargantuaX" style="width:45px">
<img src="https://github.com/GargantuaX.png?size=45" width="45"/>
</a>
<span id="nav-6"></span>
## 安装
Wails 使用 cgo 与原生渲染引擎结合,因此需要依赖一些平台的库以及 Go 的安装。基本要求是:
- Go 1.16
- npm
<span id="nav-6-1"></span>
### MacOS
请确保已安装 xcode 命令行工具。这可以通过运行下面的命令来完成:
`xcode-select --install`
<span id="nav-6-2"></span>
### Linux
<span id="nav-6-2-1"></span>
#### Debian/Ubuntu
`sudo apt install libgtk-3-dev libwebkit2gtk-4.0-dev`
_Debian: 8, 9, 10_
_Ubuntu: 16.04, 18.04, 19.04_
_也成功测试了: Zorin 15, Parrot 4.7, Linuxmint 19, Elementary 5, Kali, Neon_, Pop!\_OS
<span id="nav-6-2-2"></span>
#### Arch Linux / ArchLabs / Ctlos Linux
`sudo pacman -S webkit2gtk gtk3`
_也成功测试了: Manjaro & ArcoLinux_
<span id="nav-6-2-3"></span>
#### Centos
`sudo yum install webkitgtk3-devel gtk3-devel`
_CentOS 6, 7_
<span id="nav-6-2-4"></span>
#### Fedora
`sudo yum install webkit2gtk3-devel gtk3-devel`
_Fedora 29, 30_
<span id="nav-6-2-5"></span>
#### VoidLinux & VoidLinux-musl
`xbps-install gtk+3-devel webkit2gtk-devel`
<span id="nav-6-2-6"></span>
#### Gentoo
`sudo emerge gtk+:3 webkit-gtk`
<span id="nav-6-3"></span>
### Windows
Windows 需要 GCC 和相关工具。 建议从 [http://tdm-gcc.tdragon.net/download](http://tdm-gcc.tdragon.net/download) 下载, 安装完成,您就可以开始了。
<span id="nav-7"></span>
## 使用方法
**确保 Go modules 是开启的GO111MODULE=on 并且 go/bin 在您的 PATH 变量中。**
安装很简单,运行以下命令:
```
go get -u github.com/wailsapp/wails/cmd/wails
```
<span id="nav-7-1"></span>
### 下一步
建议在此时阅读 [https://wails.app](https://wails.app) 上面的文档.
<span id="nav-8"></span>
## 常见问题
- 它是 Electron 的替代品吗?
取决于您的要求。它旨在使 Go 程序员可以轻松制作轻量级桌面应用程序或在其现有应用程序中添加前端。尽管 Wails 当前不提供对诸如菜单之类的原生元素的钩子,但将来可能会改变。
- 这个项目针对的是哪些人?
希望将 HTML / JS / CSS 前端与其应用程序捆绑在一起的程序员,而不是借助创建服务并打开浏览器进行查看的方式。
- 名字怎么来的?
当我看到 WebView 时,我想"我真正想要的是围绕构建 WebView 应用程序工作,有点像 Rails 对于 Ruby"。因此最初它是一个文字游戏Webview on
Rails。碰巧也是我来自的 [国家](https://en.wikipedia.org/wiki/Wales) 的英文名字的同音。所以就是它了。
<span id="nav-9"></span>
## 贡献者
<a href="https://github.com/qaisjp"><img src="https://github.com/qaisjp.png?size=40" width="40"/></a>
<a href="https://github.com/alee792"><img src="https://github.com/alee792.png?size=40" width="40"/></a>
<a href="https://github.com/lanzafame"><img src="https://github.com/lanzafame.png?size=40" width="40"/></a>
<a href="https://github.com/mattn"><img src="https://github.com/mattn.png?size=40" width="40"/></a>
<a href="https://github.com/0xflotus"><img src="https://github.com/0xflotus.png?size=40" width="40"/></a>
<a href="https://github.com/mdhender"><img src="https://github.com/mdhender.png?size=40" width="40"/></a>
<a href="https://github.com/fishfishfish2104"><img src="https://github.com/fishfishfish2104.png?size=40" width="40"/></a>
<a href="https://github.com/intelwalk"><img src="https://github.com/intelwalk.png?size=40" width="40"/></a>
<a href="https://github.com/ocelotsloth"><img src="https://github.com/ocelotsloth.png?size=40" width="40"/></a>
<a href="https://github.com/bh90210"><img src="https://github.com/bh90210.png?size=40" width="40"/></a>
<a href="https://github.com/iceleo-com"><img src="https://github.com/iceleo-com.png?size=40" width="40"/></a>
<a href="https://github.com/fallendusk"><img src="https://github.com/fallendusk.png?size=40" width="40"/></a>
<a href="https://github.com/Chronophylos"><img src="https://github.com/Chronophylos.png?size=40" width="40"/></a>
<a href="https://github.com/Vaelatern"><img src="https://github.com/Vaelatern.png?size=40" width="40"/></a>
<a href="https://github.com/mewmew"><img src="https://github.com/mewmew.png?size=40" width="40"/></a>
<a href="https://github.com/kraney"><img src="https://github.com/kraney.png?size=40" width="40"/></a>
<a href="https://github.com/JackMordaunt"><img src="https://github.com/JackMordaunt.png?size=40" width="40"/></a>
<a href="https://github.com/MichaelHipp"><img src="https://github.com/MichaelHipp.png?size=40" width="40"/></a>
<a href="https://github.com/tmclane"><img src="https://github.com/tmclane.png?size=40" width="40"/></a>
<a href="https://github.com/Rested"><img src="https://github.com/Rested.png?size=40" width="40"/></a>
<a href="https://github.com/Jarek-SRT"><img src="https://github.com/Jarek-SRT.png?size=40" width="40"/></a>
<a href="https://github.com/konez2k"><img src="https://github.com/konez2k.png?size=40" width="40"/></a>
<a href="https://github.com/sayuthisobri"><img src="https://github.com/sayuthisobri.png?size=40" width="40"/></a>
<a href="https://github.com/dedo1911"><img src="https://github.com/dedo1911.png?size=40" width="40"/></a>
<a href="https://github.com/fdidron"><img src="https://github.com/fdidron.png?size=40" width="40"/></a>
<a href="https://github.com/Splode"><img src="https://github.com/Splode.png?size=40" width="40"/></a>
<a href="https://github.com/Lyimmi"><img src="https://github.com/Lyimmi.png?size=40" width="40"/></a>
<a href="https://github.com/Unix4ever"><img src="https://github.com/Unix4ever.png?size=40" width="40"/></a>
<a href="https://github.com/timkippdev"><img src="https://github.com/timkippdev.png?size=40" width="40"/></a>
<a href="https://github.com/kyoto44"><img src="https://github.com/kyoto44.png?size=40" width="40"/></a>
<a href="https://github.com/artooro"><img src="https://github.com/artooro.png?size=40" width="40"/></a>
<a href="https://github.com/ilgityildirim"><img src="https://github.com/ilgityildirim.png?size=40" width="40"/></a>
<a href="https://github.com/gelleson"><img src="https://github.com/gelleson.png?size=40" width="40"/></a>
<a href="https://github.com/kmuchmore"><img src="https://github.com/kmuchmore.png?size=40" width="40"/></a>
<a href="https://github.com/aayush420"><img src="https://github.com/aayush420.png?size=40" width="40"/></a>
<a href="https://github.com/Rezrazi"><img src="https://github.com/Rezrazi.png?size=40" width="40"/></a>
<a href="https://github.com/misitebao"><img src="https://github.com/misitebao.png?size=40" width="40"/></a>
<a href="https://github.com/DrunkenPoney"><img src="https://github.com/DrunkenPoney.png?size=40" width="40"/></a>
<a href="https://github.com/SophieAu"><img src="https://github.com/SophieAu.png?size=40" width="40"/></a>
<a href="https://github.com/alexmat"><img src="https://github.com/alexmat.png?size=40" width="40"/></a>
<a href="https://github.com/RH12503"><img src="https://github.com/RH12503.png?size=40" width="40"/></a>
<a href="https://github.com/hi019"><img src="https://github.com/hi019.png?size=40" width="40"/></a>
<a href="https://github.com/Igogrek"><img src="https://github.com/Igogrek.png?size=40" width="40"/></a>
<a href="https://github.com/aschey"><img src="https://github.com/aschey.png?size=40" width="40"/></a>
<a href="https://github.com/akhudek"><img src="https://github.com/akhudek.png?size=40" width="40"/></a>
<a href="https://github.com/s12chung"><img src="https://github.com/s12chung.png?size=40" width="40"/></a>
<span id="nav-10"></span>
## 特别提及
如果没有以下人员,此项目或许永远不会存在:
- [Dustin Krysak](https://wiki.ubuntu.com/bashfulrobot) - 他的支持和反馈是巨大的。
- [Serge Zaitsev](https://github.com/zserge) - Wails 窗口所使用的 [Webview](https://github.com/zserge/webview) 的作者。
- [Byron](https://github.com/bh90210) - 有时Byron 一个人保持这个项目活跃着。没有他令人难以置信的投入,我们永远不会得到 v1 。
编写项目代码时伴随着以下专辑:
- [Manic Street Preachers - Resistance Is Futile](https://open.spotify.com/album/1R2rsEUqXjIvAbzM0yHrxA)
- [Manic Street Preachers - This Is My Truth, Tell Me Yours](https://open.spotify.com/album/4VzCL9kjhgGQeKCiojK1YN)
- [The Midnight - Endless Summer](https://open.spotify.com/album/4Krg8zvprquh7TVn9OxZn8)
- [Gary Newman - Savage (Songs from a Broken World)](https://open.spotify.com/album/3kMfsD07Q32HRWKRrpcexr)
- [Steve Vai - Passion & Warfare](https://open.spotify.com/album/0oL0OhrE2rYVns4IGj8h2m)
- [Ben Howard - Every Kingdom](https://open.spotify.com/album/1nJsbWm3Yy2DW1KIc1OKle)
- [Ben Howard - Noonday Dream](https://open.spotify.com/album/6astw05cTiXEc2OvyByaPs)
- [Adwaith - Melyn](https://open.spotify.com/album/2vBE40Rp60tl7rNqIZjaXM)
- [Gwidaith Hen Fran - Cedors Hen Wrach](https://open.spotify.com/album/3v2hrfNGINPLuDP0YDTOjm)
- [Metallica - Metallica](https://open.spotify.com/album/2Kh43m04B1UkVcpcRa1Zug)
- [Bloc Party - Silent Alarm](https://open.spotify.com/album/6SsIdN05HQg2GwYLfXuzLB)
- [Maxthor - Another World](https://open.spotify.com/album/3tklE2Fgw1hCIUstIwPBJF)
- [Alun Tan Lan - Y Distawrwydd](https://open.spotify.com/album/0c32OywcLpdJCWWMC6vB8v)
<span id="nav-11"></span>
## 特别感谢
<p align="center" style="text-align: center">
<a href="https://pace.dev"><img src="/assets/images/pace.jpeg"/></a><br/>
<i>非常</i> 感谢<a href="https://pace.dev">Pace</a>对项目的赞助,并帮助将 Wails 移植到 Apple Silicon !<br/><br/>
如果您正在寻找一个强大并且快速和易于使用的项目管理工具,可以看看他们!<br/><br/>
</p>
<p align="center" style="text-align: center">
特别感谢 JetBrains 向我们捐赠许可!<br/><br/>
请点击 logo 让他们知道你的感激之情!<br/><br/>
<a href="https://www.jetbrains.com?from=Wails"><img src="/assets/images/jetbrains-grayscale.png" width="30%"></a>
</p>

128
app.go
View File

@@ -1,18 +1,7 @@
package wails
import (
"os"
"syscall"
"github.com/syossan27/tebata"
"github.com/wailsapp/wails/cmd"
"github.com/wailsapp/wails/lib/binding"
"github.com/wailsapp/wails/lib/event"
"github.com/wailsapp/wails/lib/interfaces"
"github.com/wailsapp/wails/lib/ipc"
"github.com/wailsapp/wails/lib/logger"
"github.com/wailsapp/wails/lib/renderer"
wailsruntime "github.com/wailsapp/wails/runtime"
)
// -------------------------------- Compile time Flags ------------------------------
@@ -20,29 +9,24 @@ import (
// BuildMode indicates what mode we are in
var BuildMode = cmd.BuildModeProd
// Runtime is the Go Runtime struct
type Runtime = wailsruntime.Runtime
// Store is a state store used for syncing with
// the front end
type Store = wailsruntime.Store
// CustomLogger is a specialised logger
type CustomLogger = logger.CustomLogger
// ----------------------------------------------------------------------------------
// App defines the main application struct
type App struct {
config *AppConfig // The Application configuration object
cli *cmd.Cli // In debug mode, we have a cli
renderer interfaces.Renderer // The renderer is what we will render the app to
logLevel string // The log level of the app
ipc interfaces.IPCManager // Handles the IPC calls
log *logger.CustomLogger // Logger
bindingManager interfaces.BindingManager // Handles binding of Go code to renderer
eventManager interfaces.EventManager // Handles all the events
runtime interfaces.Runtime // The runtime object for registered structs
config *AppConfig // The Application configuration object
cli *cmd.Cli // In debug mode, we have a cli
renderer Renderer // The renderer is what we will render the app to
logLevel string // The log level of the app
ipc *ipcManager // Handles the IPC calls
log *CustomLogger // Logger
bindingManager *bindingManager // Handles binding of Go code to renderer
eventManager *eventManager // Handles all the events
runtime *Runtime // The runtime object for registered structs
// This is a list of all the JS/CSS that needs injecting
// It will get injected in order
jsCache []string
cssCache []string
}
// CreateApp creates the application window with the given configuration
@@ -54,15 +38,15 @@ func CreateApp(optionalConfig ...*AppConfig) *App {
}
result := &App{
logLevel: "debug",
renderer: renderer.NewWebView(),
ipc: ipc.NewManager(),
bindingManager: binding.NewManager(),
eventManager: event.NewManager(),
log: logger.NewCustomLogger("App"),
logLevel: "info",
renderer: &webViewRenderer{},
ipc: newIPCManager(),
bindingManager: newBindingManager(),
eventManager: newEventManager(),
log: newCustomLogger("App"),
}
appconfig, err := newConfig(userConfig)
appconfig, err := newAppConfig(userConfig)
if err != nil {
result.log.Fatalf("Cannot use custom HTML: %s", err.Error())
}
@@ -76,15 +60,11 @@ func CreateApp(optionalConfig ...*AppConfig) *App {
result.config.DisableInspector = true
}
// Platform specific init
platformInit()
return result
}
// Run the app
func (a *App) Run() error {
if BuildMode != cmd.BuildModeProd {
return a.cli.Run()
}
@@ -100,14 +80,14 @@ func (a *App) Run() error {
func (a *App) start() error {
// Set the log level
logger.SetLogLevel(a.logLevel)
setLogLevel(a.logLevel)
// Log starup
a.log.Info("Starting")
// Check if we are to run in bridge mode
// Check if we are to run in headless mode
if BuildMode == cmd.BuildModeBridge {
a.renderer = renderer.NewBridge()
a.renderer = &Headless{}
}
// Initialise the renderer
@@ -116,59 +96,45 @@ func (a *App) start() error {
return err
}
// Start signal handler
t := tebata.New(os.Interrupt, os.Kill, syscall.SIGTERM, syscall.SIGINT, syscall.SIGKILL)
t.Reserve(func() {
a.log.Debug("SIGNAL CAUGHT! Starting Shutdown")
a.renderer.Close()
})
// Start event manager and give it our renderer
a.eventManager.Start(a.renderer)
a.eventManager.start(a.renderer)
// Start the IPC Manager and give it the event manager and binding manager
a.ipc.Start(a.eventManager, a.bindingManager)
a.ipc.start(a.eventManager, a.bindingManager)
// Create the runtime
a.runtime = wailsruntime.NewRuntime(a.eventManager, a.renderer)
a.runtime = newRuntime(a.eventManager, a.renderer)
// Start binding manager and give it our renderer
err = a.bindingManager.Start(a.renderer, a.runtime)
err = a.bindingManager.start(a.renderer, a.runtime)
if err != nil {
return err
}
// Defer the shutdown
defer a.shutdown()
// Inject CSS
a.renderer.AddCSSList(a.cssCache)
// Inject JS
a.renderer.AddJSList(a.jsCache)
// Run the renderer
err = a.renderer.Run()
if err != nil {
return err
}
return nil
}
// shutdown the app
func (a *App) shutdown() {
// Make sure this is only called once
a.log.Debug("Shutting down")
// Shutdown Binding Manager
a.bindingManager.Shutdown()
// Shutdown IPC Manager
a.ipc.Shutdown()
// Shutdown Event Manager
a.eventManager.Shutdown()
a.log.Debug("Cleanly Shutdown")
return a.renderer.Run()
}
// Bind allows the user to bind the given object
// with the application
func (a *App) Bind(object interface{}) {
a.bindingManager.Bind(object)
a.bindingManager.bind(object)
}
// AddJS adds a piece of Javascript to a cache that
// gets injected at runtime
func (a *App) AddJS(js string) {
a.jsCache = append(a.jsCache, js)
}
// AddCSS adds a CSS string to a cache that
// gets injected at runtime
func (a *App) AddCSS(js string) {
a.cssCache = append(a.cssCache, js)
}

View File

@@ -11,9 +11,10 @@ func (app *App) setupCli() *cmd.Cli {
result := cmd.NewCli(app.config.Title, "Debug build")
result.Version(cmd.Version)
// Setup cli to handle loglevel
// Setup cli to handle loglevel and headless flags
result.
StringFlag("loglevel", "Sets the log level [debug|info|error|panic|fatal]. Default debug", &app.logLevel).
// BoolFlag("headless", "Runs the app in headless mode", &app.headless).
Action(app.start)
// Banner

97
app_config.go Normal file
View File

@@ -0,0 +1,97 @@
package wails
import (
"strings"
"github.com/dchest/htmlmin"
"github.com/leaanthony/mewn"
)
// AppConfig is the configuration structure used when creating a Wails App object
type AppConfig struct {
Width, Height int
Title string
defaultHTML string
HTML string
JS string
CSS string
Colour string
Resizable bool
DisableInspector bool
isHTMLFragment bool
}
func (a *AppConfig) merge(in *AppConfig) error {
if in.CSS != "" {
a.CSS = in.CSS
}
if in.Title != "" {
a.Title = in.Title
}
if in.HTML != "" {
minified, err := htmlmin.Minify([]byte(in.HTML), &htmlmin.Options{
MinifyScripts: true,
})
if err != nil {
return err
}
inlineHTML := string(minified)
inlineHTML = strings.Replace(inlineHTML, "'", "\\'", -1)
inlineHTML = strings.Replace(inlineHTML, "\n", " ", -1)
a.HTML = strings.TrimSpace(inlineHTML)
// Deduce whether this is a full html page or a fragment
// The document is determined to be a fragment if an HTML
// tag exists and is located before the first div tag
HTMLTagIndex := strings.Index(a.HTML, "<html")
DivTagIndex := strings.Index(a.HTML, "<div")
if HTMLTagIndex == -1 {
a.isHTMLFragment = true
} else {
if DivTagIndex < HTMLTagIndex {
a.isHTMLFragment = true
}
}
}
if in.Colour != "" {
a.Colour = in.Colour
}
if in.JS != "" {
a.JS = in.JS
}
if in.Width != 0 {
a.Width = in.Width
}
if in.Height != 0 {
a.Height = in.Height
}
a.Resizable = in.Resizable
a.DisableInspector = in.DisableInspector
return nil
}
// Creates the default configuration
func newAppConfig(userConfig *AppConfig) (*AppConfig, error) {
result := &AppConfig{
Width: 800,
Height: 600,
Resizable: true,
Title: "My Wails App",
Colour: "#FFF", // White by default
HTML: mewn.String("./wailsruntimeassets/default/default.html"),
}
if userConfig != nil {
err := result.merge(userConfig)
if err != nil {
return nil, err
}
}
return result, nil
}

View File

@@ -1,8 +0,0 @@
//go:build linux || darwin || !windows
// +build linux darwin !windows
package wails
func platformInit() {
}

View File

@@ -1,28 +0,0 @@
//go:build windows || !linux || !darwin
// +build windows !linux !darwin
package wails
import (
"fmt"
"log"
"syscall"
)
func platformInit() {
err := SetProcessDPIAware()
if err != nil {
log.Fatalf(err.Error())
}
}
// SetProcessDPIAware via user32.dll
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setprocessdpiaware
// Also, thanks Jack Mordaunt! https://github.com/wailsapp/wails/issues/293
func SetProcessDPIAware() error {
status, r, err := syscall.NewLazyDLL("user32.dll").NewProc("SetProcessDPIAware").Call()
if status == 0 {
return fmt.Errorf("exit status %d: %v %v", status, r, err)
}
return nil
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

View File

@@ -1,4 +1,4 @@
package binding
package wails
import (
"bytes"
@@ -6,9 +6,6 @@ import (
"fmt"
"reflect"
"runtime"
"strings"
"github.com/wailsapp/wails/lib/logger"
)
type boundFunction struct {
@@ -17,7 +14,7 @@ type boundFunction struct {
functionType reflect.Type
inputs []reflect.Type
returnTypes []reflect.Type
log *logger.CustomLogger
log *CustomLogger
hasErrorReturnType bool
}
@@ -28,16 +25,12 @@ func newBoundFunction(object interface{}) (*boundFunction, error) {
objectType := reflect.TypeOf(object)
name := runtime.FuncForPC(objectValue.Pointer()).Name()
if strings.Contains(name, "/") {
parts := strings.Split(name, "/")
name = parts[len(parts)-1]
}
result := &boundFunction{
fullName: name,
function: objectValue,
functionType: objectType,
log: logger.NewCustomLogger(name),
log: newCustomLogger(name),
}
err := result.processParameters()
@@ -62,7 +55,7 @@ func (b *boundFunction) processParameters() error {
b.inputs[index] = param
typ := param
index := index
b.log.DebugFields("Input param", logger.Fields{
b.log.DebugFields("Input param", Fields{
"index": index,
"name": name,
"kind": kind,

View File

@@ -1,55 +1,45 @@
package binding
package wails
import (
"fmt"
"os"
"path/filepath"
"reflect"
"strings"
"unicode"
"github.com/wailsapp/wails/lib/interfaces"
"github.com/wailsapp/wails/lib/logger"
"github.com/wailsapp/wails/lib/messages"
)
var typescriptDefinitionFilename = ""
/**
// Manager handles method binding
type Manager struct {
binding:
Name() // Full name (package+name)
Call(params)
**/
type bindingManager struct {
methods map[string]*boundMethod
functions map[string]*boundFunction
internalMethods *internalMethods
initMethods []*boundMethod
shutdownMethods []*boundMethod
log *logger.CustomLogger
renderer interfaces.Renderer
runtime interfaces.Runtime // The runtime object to pass to bound structs
log *CustomLogger
renderer Renderer
runtime *Runtime // The runtime object to pass to bound structs
objectsToBind []interface{}
bindPackageNames bool // Package name should be considered when binding
structList map[string][]string // structList["mystruct"] = []string{"Method1", "Method2"}
bindPackageNames bool // Package name should be considered when binding
}
// NewManager creates a new Manager struct
func NewManager() interfaces.BindingManager {
result := &Manager{
methods: make(map[string]*boundMethod),
functions: make(map[string]*boundFunction),
log: logger.NewCustomLogger("Bind"),
internalMethods: newInternalMethods(),
structList: make(map[string][]string),
func newBindingManager() *bindingManager {
result := &bindingManager{
methods: make(map[string]*boundMethod),
functions: make(map[string]*boundFunction),
log: newCustomLogger("Bind"),
}
return result
}
// BindPackageNames sets a flag to indicate package names should be considered when binding
func (b *Manager) BindPackageNames() {
// Sets flag to indicate package names should be considered when binding
func (b *bindingManager) BindPackageNames() {
b.bindPackageNames = true
}
// Start the binding manager
func (b *Manager) Start(renderer interfaces.Renderer, runtime interfaces.Runtime) error {
func (b *bindingManager) start(renderer Renderer, runtime *Runtime) error {
b.log.Info("Starting")
b.renderer = renderer
b.runtime = runtime
@@ -62,7 +52,7 @@ func (b *Manager) Start(renderer interfaces.Renderer, runtime interfaces.Runtime
return err
}
func (b *Manager) initialise() error {
func (b *bindingManager) initialise() error {
var err error
// var binding *boundMethod
@@ -96,58 +86,11 @@ func (b *Manager) initialise() error {
return err
}
}
// If we wish to generate a typescript definition file...
if typescriptDefinitionFilename != "" {
err := b.generateTypescriptDefinitions()
if err != nil {
return err
}
}
return nil
}
// Generate typescript
func (b *Manager) generateTypescriptDefinitions() error {
var output strings.Builder
for structname, methodList := range b.structList {
structname = strings.SplitN(structname, ".", 2)[1]
output.WriteString(fmt.Sprintf("interface %s {\n", structname))
for _, method := range methodList {
output.WriteString(fmt.Sprintf("\t%s(...args : any[]):Promise<any>\n", method))
}
output.WriteString("}\n")
}
output.WriteString("\n")
output.WriteString("interface Backend {\n")
for structname := range b.structList {
structname = strings.SplitN(structname, ".", 2)[1]
output.WriteString(fmt.Sprintf("\t%[1]s: %[1]s\n", structname))
}
output.WriteString("}\n")
globals := `
declare global {
interface Window {
backend: Backend;
}
}
export {};`
output.WriteString(globals)
b.log.Info("Written Typescript file: " + typescriptDefinitionFilename)
dir := filepath.Dir(typescriptDefinitionFilename)
os.MkdirAll(dir, 0755)
return os.WriteFile(typescriptDefinitionFilename, []byte(output.String()), 0755)
}
// bind the given struct method
func (b *Manager) bindMethod(object interface{}) error {
func (b *bindingManager) bindMethod(object interface{}) error {
objectType := reflect.TypeOf(object)
baseName := objectType.String()
@@ -159,12 +102,6 @@ func (b *Manager) bindMethod(object interface{}) error {
b.log.Debugf("Processing struct: %s", baseName)
// Calc actual name
actualName := strings.TrimPrefix(baseName, "main.")
if b.structList[actualName] == nil {
b.structList[actualName] = []string{}
}
// Iterate over method definitions
for i := 0; i < objectType.NumMethod(); i++ {
@@ -174,8 +111,6 @@ func (b *Manager) bindMethod(object interface{}) error {
fullMethodName := baseName + "." + methodName
method := reflect.ValueOf(object).MethodByName(methodName)
b.structList[actualName] = append(b.structList[actualName], methodName)
// Skip unexported methods
if !unicode.IsUpper([]rune(methodName)[0]) {
continue
@@ -191,9 +126,6 @@ func (b *Manager) bindMethod(object interface{}) error {
if newMethod.isWailsInit {
b.log.Debugf("Detected WailsInit function: %s", fullMethodName)
b.initMethods = append(b.initMethods, newMethod)
} else if newMethod.isWailsShutdown {
b.log.Debugf("Detected WailsShutdown function: %s", fullMethodName)
b.shutdownMethods = append(b.shutdownMethods, newMethod)
} else {
// Save boundMethod
b.log.Infof("Bound Method: %s()", fullMethodName)
@@ -208,7 +140,7 @@ func (b *Manager) bindMethod(object interface{}) error {
}
// bind the given function object
func (b *Manager) bindFunction(object interface{}) error {
func (b *bindingManager) bindFunction(object interface{}) error {
newFunction, err := newBoundFunction(object)
if err != nil {
@@ -225,18 +157,13 @@ func (b *Manager) bindFunction(object interface{}) error {
return nil
}
// Bind saves the given object to be bound at start time
func (b *Manager) Bind(object interface{}) {
// Save the given object to be bound at start time
func (b *bindingManager) bind(object interface{}) {
// Store binding
b.objectsToBind = append(b.objectsToBind, object)
}
func (b *Manager) processInternalCall(callData *messages.CallData) (interface{}, error) {
// Strip prefix
return b.internalMethods.processCall(callData)
}
func (b *Manager) processFunctionCall(callData *messages.CallData) (interface{}, error) {
func (b *bindingManager) processFunctionCall(callData *callData) (interface{}, error) {
// Return values
var result []reflect.Value
var err error
@@ -262,14 +189,10 @@ func (b *Manager) processFunctionCall(callData *messages.CallData) (interface{},
return nil, errorResult.Interface().(error)
}
}
// fmt.Printf("result = '%+v'\n", result)
if len(result) > 0 {
return result[0].Interface(), nil
}
return nil, nil
return result[0].Interface(), nil
}
func (b *Manager) processMethodCall(callData *messages.CallData) (interface{}, error) {
func (b *bindingManager) processMethodCall(callData *callData) (interface{}, error) {
// Return values
var result []reflect.Value
var err error
@@ -303,8 +226,8 @@ func (b *Manager) processMethodCall(callData *messages.CallData) (interface{}, e
return nil, nil
}
// ProcessCall processes the given call request
func (b *Manager) ProcessCall(callData *messages.CallData) (result interface{}, err error) {
// process an incoming call request
func (b *bindingManager) processCall(callData *callData) (result interface{}, err error) {
b.log.Debugf("Wanting to call %s", callData.BindingName)
// Determine if this is function call or method call by the number of
@@ -331,8 +254,6 @@ func (b *Manager) ProcessCall(callData *messages.CallData) (result interface{},
result, err = b.processFunctionCall(callData)
case 2:
result, err = b.processMethodCall(callData)
case 3:
result, err = b.processInternalCall(callData)
default:
result = nil
err = fmt.Errorf("Invalid binding name '%s'", callData.BindingName)
@@ -342,7 +263,7 @@ func (b *Manager) ProcessCall(callData *messages.CallData) (result interface{},
// callWailsInitMethods calls all of the WailsInit methods that were
// registered with the runtime object
func (b *Manager) callWailsInitMethods() error {
func (b *bindingManager) callWailsInitMethods() error {
// Create reflect value for runtime object
runtimeValue := reflect.ValueOf(b.runtime)
params := []reflect.Value{runtimeValue}
@@ -359,13 +280,3 @@ func (b *Manager) callWailsInitMethods() error {
}
return nil
}
// Shutdown the binding manager
func (b *Manager) Shutdown() {
b.log.Debug("Shutdown called")
for _, method := range b.shutdownMethods {
b.log.Debugf("Calling Shutdown for method: %s", method.fullName)
method.call("[]")
}
b.log.Debug("Shutdown complete")
}

View File

@@ -1,12 +1,10 @@
package binding
package wails
import (
"bytes"
"encoding/json"
"fmt"
"reflect"
"github.com/wailsapp/wails/lib/logger"
)
type boundMethod struct {
@@ -15,10 +13,9 @@ type boundMethod struct {
method reflect.Value
inputs []reflect.Type
returnTypes []reflect.Type
log *logger.CustomLogger
log *CustomLogger
hasErrorReturnType bool // Indicates if there is an error return type
isWailsInit bool
isWailsShutdown bool
}
// Creates a new bound method based on the given method + type
@@ -30,7 +27,7 @@ func newBoundMethod(name string, fullName string, method reflect.Value, objectTy
}
// Setup logger
result.log = logger.NewCustomLogger(result.fullName)
result.log = newCustomLogger(result.fullName)
// Check if Parameters are valid
err := result.processParameters()
@@ -40,11 +37,6 @@ func newBoundMethod(name string, fullName string, method reflect.Value, objectTy
err = result.processWailsInit()
}
// Are we a WailsShutdown method?
if result.Name == "WailsShutdown" {
err = result.processWailsShutdown()
}
return result, err
}
@@ -65,7 +57,7 @@ func (b *boundMethod) processParameters() error {
b.inputs[index] = param
typ := param
index := index
b.log.DebugFields("Input param", logger.Fields{
b.log.DebugFields("Input param", Fields{
"index": index,
"name": name,
"kind": kind,
@@ -174,10 +166,10 @@ func (b *boundMethod) setInputValue(index int, typ reflect.Type, val interface{}
reflect.Map,
reflect.Ptr,
reflect.Slice:
b.log.Debug("Converting nil to type")
logger.Debug("Converting nil to type")
result = reflect.ValueOf(val).Convert(typ)
default:
b.log.Debug("Cannot convert nil to type, returning error")
logger.Debug("Cannot convert nil to type, returning error")
return reflect.Zero(typ), fmt.Errorf("Unable to use null value for parameter %d of method %s", index+1, b.fullName)
}
} else {
@@ -196,7 +188,7 @@ func (b *boundMethod) processWailsInit() error {
// It must be *wails.Runtime
inputName := b.inputs[0].String()
b.log.Debugf("WailsInit input type: %s", inputName)
if inputName != "*runtime.Runtime" {
if inputName != "*wails.Runtime" {
return fmt.Errorf("Invalid WailsInit() definition. Expected input to be wails.Runtime, but got %s", inputName)
}
@@ -217,20 +209,3 @@ func (b *boundMethod) processWailsInit() error {
return nil
}
func (b *boundMethod) processWailsShutdown() error {
// We must not have any inputs
if len(b.inputs) != 0 {
return fmt.Errorf("Invalid WailsShutdown() definition. Expected 0 inputs, but got %d", len(b.inputs))
}
// We must have only 1 output, it must be error
if len(b.returnTypes) != 0 {
return fmt.Errorf("Invalid WailsShutdown() definition. Expected 0 return types, but got %d", len(b.returnTypes))
}
// We are indeed a wails Shutdown method
b.isWailsShutdown = true
return nil
}

View File

@@ -96,7 +96,6 @@ type Command struct {
flagCount int
log *Logger
helpFlag bool
hidden bool
}
// NewCommand creates a new Command
@@ -107,7 +106,6 @@ func NewCommand(name string, description string, app *Cli, parentCommandPath str
SubCommandsMap: make(map[string]*Command),
App: app,
log: NewLogger(),
hidden: false,
}
// Set up command path
@@ -214,9 +212,6 @@ func (c *Command) PrintHelp() {
c.log.White("Available commands:")
fmt.Println("")
for _, subcommand := range c.SubCommands {
if subcommand.isHidden() {
continue
}
spacer := strings.Repeat(" ", 3+c.longestSubcommand-len(subcommand.Name))
isDefault := ""
if subcommand.isDefaultCommand() {
@@ -242,16 +237,6 @@ func (c *Command) isDefaultCommand() bool {
return c.App.defaultCommand == c
}
// isHidden returns true if the command is a hidden command
func (c *Command) isHidden() bool {
return c.hidden
}
// Hidden hides the command from the Help system
func (c *Command) Hidden() {
c.hidden = true
}
// Command - Defines a subcommand
func (c *Command) Command(name, description string) *Command {
result := NewCommand(name, description, c.App, c.CommandPath)

31
cmd/cmd-mewn.go Normal file

File diff suppressed because one or more lines are too long

100
cmd/fs.go
View File

@@ -1,19 +1,13 @@
package cmd
import (
"bytes"
"crypto/md5"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"path"
"path/filepath"
"runtime"
"strings"
"github.com/leaanthony/slicer"
)
// FSHelper - Wrapper struct for File System utility commands
@@ -47,28 +41,12 @@ func (fs *FSHelper) FileExists(path string) bool {
return fi.Mode().IsRegular()
}
// FindFile returns the first occurrence of match inside path.
func (fs *FSHelper) FindFile(path, match string) (string, error) {
files, err := os.ReadDir(path)
if err != nil {
return "", err
}
for _, f := range files {
if !f.IsDir() && strings.Contains(f.Name(), match) {
return f.Name(), nil
}
}
return "", fmt.Errorf("file not found")
}
// CreateFile creates a file at the given filename location with the contents
// set to the given data. It will create intermediary directories if needed.
func (fs *FSHelper) CreateFile(filename string, data []byte) error {
// Ensure directory exists
fs.MkDirs(filepath.Dir(filename))
return os.WriteFile(filename, data, 0644)
return ioutil.WriteFile(filename, data, 0644)
}
// MkDirs creates the given nested directories.
@@ -116,43 +94,21 @@ func (fs *FSHelper) RemoveFile(filename string) error {
}
// RemoveFiles removes the given filenames
func (fs *FSHelper) RemoveFiles(files []string, continueOnError bool) error {
func (fs *FSHelper) RemoveFiles(files []string) error {
for _, filename := range files {
err := os.Remove(filename)
if err != nil && !continueOnError {
if err != nil {
return err
}
}
return nil
}
// Dir holds information about a directory
type Dir struct {
localPath string
fullPath string
}
// Directory creates a new Dir struct with the given directory path
func (fs *FSHelper) Directory(dir string) (*Dir, error) {
fullPath, err := filepath.Abs(dir)
return &Dir{fullPath: fullPath}, err
}
// LocalDir creates a new Dir struct based on a path relative to the caller
func (fs *FSHelper) LocalDir(dir string) (*Dir, error) {
_, filename, _, _ := runtime.Caller(1)
fullPath, err := filepath.Abs(filepath.Join(path.Dir(filename), dir))
return &Dir{
localPath: dir,
fullPath: fullPath,
}, err
}
// GetSubdirs will return a list of FQPs to subdirectories in the given directory
func (d *Dir) GetSubdirs() (map[string]string, error) {
func (fs *FSHelper) GetSubdirs(dir string) (map[string]string, error) {
// Read in the directory information
fileInfo, err := os.ReadDir(d.fullPath)
fileInfo, err := ioutil.ReadDir(dir)
if err != nil {
return nil, err
}
@@ -164,65 +120,25 @@ func (d *Dir) GetSubdirs() (map[string]string, error) {
// map["directoryName"] = "path/to/directoryName"
for _, file := range fileInfo {
if file.IsDir() {
subdirs[file.Name()] = filepath.Join(d.fullPath, file.Name())
subdirs[file.Name()] = filepath.Join(dir, file.Name())
}
}
return subdirs, nil
}
// GetAllFilenames returns all filename in and below this directory
func (d *Dir) GetAllFilenames() (*slicer.StringSlicer, error) {
result := slicer.String()
err := filepath.Walk(d.fullPath, func(dir string, info os.FileInfo, err error) error {
if dir == d.fullPath {
return nil
}
if err != nil {
return err
}
// Don't copy template metadata
result.Add(dir)
return nil
})
return result, err
}
// MkDir creates the given directory.
// Returns error on failure
func (fs *FSHelper) MkDir(dir string) error {
return os.Mkdir(dir, 0700)
}
// SaveAsJSON saves the JSON representation of the given data to the given filename
func (fs *FSHelper) SaveAsJSON(data interface{}, filename string) error {
var buf bytes.Buffer
e := json.NewEncoder(&buf)
e.SetEscapeHTML(false)
e.SetIndent("", " ")
e.Encode(data)
err := os.WriteFile(filename, buf.Bytes(), 0755)
if err != nil {
return err
}
return nil
}
// LoadAsString will attempt to load the given file and return
// its contents as a string
func (fs *FSHelper) LoadAsString(filename string) (string, error) {
bytes, err := fs.LoadAsBytes(filename)
bytes, err := ioutil.ReadFile(filename)
return string(bytes), err
}
// LoadAsBytes returns the contents of the file as a byte slice
func (fs *FSHelper) LoadAsBytes(filename string) ([]byte, error) {
return os.ReadFile(filename)
}
// FileMD5 returns the md5sum of the given file
func (fs *FSHelper) FileMD5(filename string) (string, error) {
f, err := os.Open(filename)

View File

@@ -3,7 +3,7 @@ package cmd
import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"sort"
)
@@ -18,17 +18,17 @@ func NewGitHubHelper() *GitHubHelper {
}
// GetVersionTags gets the list of tags on the Wails repo
// It returns a list of sorted tags in descending order
// It retuns a list of sorted tags in descending order
func (g *GitHubHelper) GetVersionTags() ([]*SemanticVersion, error) {
result := []*SemanticVersion{}
var err error
resp, err := http.Get("https://api.github.com/repos/wailsapp/wails/releases")
resp, err := http.Get("https://api.github.com/repos/wailsapp/wails/tags")
if err != nil {
return result, err
}
body, err := io.ReadAll(resp.Body)
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return result, err
}

View File

@@ -1,78 +0,0 @@
package cmd
import (
"fmt"
"path/filepath"
"regexp"
"github.com/Masterminds/semver"
)
func GetWailsVersion() (*semver.Version, error) {
var FS = NewFSHelper()
var result *semver.Version
// Load file
var err error
goModFile, err := filepath.Abs(filepath.Join(".", "go.mod"))
if err != nil {
return nil, fmt.Errorf("Unable to load go.mod at %s", goModFile)
}
goMod, err := FS.LoadAsString(goModFile)
if err != nil {
return nil, fmt.Errorf("Unable to load go.mod")
}
// Find wails version
versionRegexp := regexp.MustCompile(`.*github.com/wailsapp/wails.*(v\d+.\d+.\d+(?:-pre\d+)?)`)
versions := versionRegexp.FindStringSubmatch(goMod)
if len(versions) != 2 {
return nil, fmt.Errorf("Unable to determine Wails version")
}
version := versions[1]
result, err = semver.NewVersion(version)
if err != nil {
return nil, fmt.Errorf("Unable to parse Wails version: %s", version)
}
return result, nil
}
func GetCurrentVersion() (*semver.Version, error) {
result, err := semver.NewVersion(Version)
if err != nil {
return nil, fmt.Errorf("Unable to parse Wails version: %s", Version)
}
return result, nil
}
func GoModOutOfSync() (bool, error) {
gomodversion, err := GetWailsVersion()
if err != nil {
return true, err
}
currentVersion, err := GetCurrentVersion()
if err != nil {
return true, err
}
result := !currentVersion.Equal(gomodversion)
return result, nil
}
func UpdateGoModVersion() error {
currentVersion, err := GetCurrentVersion()
if err != nil {
return err
}
currentVersionString := currentVersion.String()
requireLine := "-require=github.com/wailsapp/wails@v" + currentVersionString
// Issue: go mod edit -require=github.com/wailsapp/wails@1.0.2-pre5
helper := NewProgramHelper()
command := []string{"go", "mod", "edit", requireLine}
return helper.RunCommandArray(command)
}

View File

@@ -2,22 +2,18 @@ package cmd
import (
"fmt"
"io/ioutil"
"os"
"os/exec"
"os/user"
"path/filepath"
"runtime"
"strconv"
"strings"
"time"
mewn "github.com/leaanthony/mewn"
"github.com/leaanthony/slicer"
"github.com/leaanthony/spinner"
wailsruntime "github.com/wailsapp/wails/runtime"
)
const xgoVersion = "1.16.3"
var fs = NewFSHelper()
// ValidateFrontendConfig checks if the frontend config is valid
@@ -39,143 +35,34 @@ func ValidateFrontendConfig(projectOptions *ProjectOptions) error {
}
// InstallGoDependencies will run go get in the current directory
func InstallGoDependencies(verbose bool) error {
var depSpinner *spinner.Spinner
if !verbose {
depSpinner = spinner.New("Ensuring Dependencies are up to date...")
depSpinner.SetSpinSpeed(50)
depSpinner.Start()
}
err := NewProgramHelper(verbose).RunCommand("go mod tidy")
func InstallGoDependencies() error {
depSpinner := spinner.New("Ensuring Dependencies are up to date...")
depSpinner.SetSpinSpeed(50)
depSpinner.Start()
err := NewProgramHelper().RunCommand("go get")
if err != nil {
if !verbose {
depSpinner.Error()
}
depSpinner.Error()
return err
}
if !verbose {
depSpinner.Success()
}
depSpinner.Success()
return nil
}
func InitializeCrossCompilation(verbose bool) error {
// Check Docker
if err := CheckIfInstalled("docker"); err != nil {
return err
}
// BuildApplication will attempt to build the project based on the given inputs
func BuildApplication(binaryName string, forceRebuild bool, buildMode string, packageApp bool, projectOptions *ProjectOptions) error {
var packSpinner *spinner.Spinner
msg := fmt.Sprintf("Pulling wailsapp/xgo:%s docker image... (may take a while)", xgoVersion)
if !verbose {
packSpinner = spinner.New(msg)
packSpinner.SetSpinSpeed(50)
packSpinner.Start()
} else {
println(msg)
}
err := NewProgramHelper(verbose).RunCommandArray([]string{"docker",
"pull", fmt.Sprintf("wailsapp/xgo:%s", xgoVersion)})
if err != nil {
if packSpinner != nil {
packSpinner.Error()
}
return err
}
if packSpinner != nil {
packSpinner.Success()
}
return nil
}
// BuildDocker builds the project using the cross compiling wailsapp/xgo:<xgoVersion> container
func BuildDocker(binaryName string, buildMode string, projectOptions *ProjectOptions) error {
var packSpinner *spinner.Spinner
if buildMode == BuildModeBridge {
return fmt.Errorf("you cant serve the application in cross-compilation")
}
// Check build directory
buildDirectory := filepath.Join(fs.Cwd(), "build")
if !fs.DirExists(buildDirectory) {
err := fs.MkDir(buildDirectory)
// Generate Windows assets if needed
if runtime.GOOS == "windows" {
cleanUp := !packageApp
err := NewPackageHelper().PackageWindows(projectOptions, cleanUp)
if err != nil {
return err
}
}
buildCommand := slicer.String()
userid := 1000
currentUser, _ := user.Current()
if i, err := strconv.Atoi(currentUser.Uid); err == nil {
userid = i
}
for _, arg := range []string{
"docker",
"run",
"--rm",
"-v", fmt.Sprintf("%s:/build", filepath.Join(fs.Cwd(), "build")),
"-v", fmt.Sprintf("%s:/source", fs.Cwd()),
"-e", fmt.Sprintf("LOCAL_USER_ID=%v", userid),
"-e", fmt.Sprintf("FLAG_TAGS=%s", projectOptions.Tags),
"-e", fmt.Sprintf("FLAG_LDFLAGS=%s", ldFlags(projectOptions, buildMode)),
"-e", "FLAG_V=false",
"-e", "FLAG_X=false",
"-e", "FLAG_RACE=false",
"-e", "FLAG_BUILDMODE=default",
"-e", "FLAG_TRIMPATH=false",
"-e", fmt.Sprintf("TARGETS=%s/%s", projectOptions.Platform, projectOptions.Architecture),
"-e", "GOPROXY=",
"-e", "GO111MODULE=on",
} {
buildCommand.Add(arg)
}
if projectOptions.GoPath != "" {
buildCommand.Add("-v")
buildCommand.Add(fmt.Sprintf("%s:/go", projectOptions.GoPath))
}
buildCommand.Add(fmt.Sprintf("wailsapp/xgo:%s", xgoVersion))
buildCommand.Add(".")
compileMessage := fmt.Sprintf(
"Packing + Compiling project for %s/%s using docker image wailsapp/xgo:%s",
projectOptions.Platform, projectOptions.Architecture, xgoVersion)
if buildMode == BuildModeDebug {
compileMessage += " (Debug Mode)"
}
if !projectOptions.Verbose {
packSpinner = spinner.New(compileMessage + "...")
packSpinner.SetSpinSpeed(50)
packSpinner.Start()
} else {
println(compileMessage)
}
err := NewProgramHelper(projectOptions.Verbose).RunCommandArray(buildCommand.AsSlice())
// Check Mewn is installed
err := CheckMewn()
if err != nil {
if packSpinner != nil {
packSpinner.Error()
}
return err
}
if packSpinner != nil {
packSpinner.Success()
}
return nil
}
// BuildNative builds on the target platform itself.
func BuildNative(binaryName string, forceRebuild bool, buildMode string, projectOptions *ProjectOptions) error {
if err := CheckWindres(); err != nil {
return err
}
@@ -185,33 +72,23 @@ func BuildNative(binaryName string, forceRebuild bool, buildMode string, project
compileMessage += " (Debug Mode)"
}
var packSpinner *spinner.Spinner
if !projectOptions.Verbose {
packSpinner = spinner.New(compileMessage + "...")
packSpinner.SetSpinSpeed(50)
packSpinner.Start()
} else {
println(compileMessage)
}
packSpinner := spinner.New(compileMessage + "...")
packSpinner.SetSpinSpeed(50)
packSpinner.Start()
buildCommand := slicer.String()
buildCommand.Add("go")
buildCommand.Add("mewn")
if buildMode == BuildModeBridge {
// Ignore errors
buildCommand.Add("-i")
}
buildCommand.Add("build")
if binaryName != "" {
// Alter binary name based on OS
switch projectOptions.Platform {
case "windows":
if !strings.HasSuffix(binaryName, ".exe") {
binaryName += ".exe"
}
default:
if strings.HasSuffix(binaryName, ".exe") {
binaryName = strings.TrimSuffix(binaryName, ".exe")
}
}
buildCommand.Add("-o", filepath.Join("build", binaryName))
buildCommand.Add("-o")
buildCommand.Add(binaryName)
}
// If we are forcing a rebuild
@@ -219,58 +96,28 @@ func BuildNative(binaryName string, forceRebuild bool, buildMode string, project
buildCommand.Add("-a")
}
buildCommand.AddSlice([]string{"-ldflags", ldFlags(projectOptions, buildMode)})
if projectOptions.Tags != "" {
buildCommand.AddSlice([]string{"--tags", projectOptions.Tags})
// Setup ld flags
ldflags := "-w -s "
if buildMode == BuildModeDebug {
ldflags = ""
}
if projectOptions.Verbose {
fmt.Printf("Command: %v\n", buildCommand.AsSlice())
// Add windows flags
if runtime.GOOS == "windows" {
ldflags += "-H windowsgui "
}
err := NewProgramHelper(projectOptions.Verbose).RunCommandArray(buildCommand.AsSlice())
if err != nil {
if packSpinner != nil {
packSpinner.Error()
}
return err
}
if packSpinner != nil {
packSpinner.Success()
}
return nil
}
// BuildApplication will attempt to build the project based on the given inputs
func BuildApplication(binaryName string, forceRebuild bool, buildMode string, packageApp bool, projectOptions *ProjectOptions) error {
var err error
if projectOptions.CrossCompile {
if err := InitializeCrossCompilation(projectOptions.Verbose); err != nil {
return err
}
}
helper := NewPackageHelper(projectOptions.Platform)
// Generate windows resources
if projectOptions.Platform == "windows" {
if err := helper.PackageWindows(projectOptions, false); err != nil {
return err
}
}
if projectOptions.CrossCompile {
err = BuildDocker(binaryName, buildMode, projectOptions)
} else {
err = BuildNative(binaryName, forceRebuild, buildMode, projectOptions)
}
ldflags += "-X github.com/wailsapp/wails.BuildMode=" + buildMode
buildCommand.AddSlice([]string{"-ldflags", ldflags})
err = NewProgramHelper().RunCommandArray(buildCommand.AsSlice())
if err != nil {
packSpinner.Error()
return err
}
packSpinner.Success()
// packageApp
if packageApp {
err = PackageApplication(projectOptions)
if err != nil {
@@ -283,52 +130,61 @@ func BuildApplication(binaryName string, forceRebuild bool, buildMode string, pa
// PackageApplication will attempt to package the application in a platform dependent way
func PackageApplication(projectOptions *ProjectOptions) error {
var packageSpinner *spinner.Spinner
if projectOptions.Verbose {
packageSpinner = spinner.New("Packaging application...")
packageSpinner.SetSpinSpeed(50)
packageSpinner.Start()
}
err := NewPackageHelper(projectOptions.Platform).Package(projectOptions)
if err != nil {
if packageSpinner != nil {
packageSpinner.Error()
// Package app
message := "Generating .app"
if runtime.GOOS == "windows" {
err := CheckWindres()
if err != nil {
return err
}
message = "Generating resource bundle"
}
packageSpinner := spinner.New(message)
packageSpinner.SetSpinSpeed(50)
packageSpinner.Start()
err := NewPackageHelper().Package(projectOptions)
if err != nil {
packageSpinner.Error()
return err
}
if packageSpinner != nil {
packageSpinner.Success()
}
packageSpinner.Success()
return nil
}
// BuildFrontend runs the given build command
func BuildFrontend(projectOptions *ProjectOptions) error {
var buildFESpinner *spinner.Spinner
if !projectOptions.Verbose {
buildFESpinner = spinner.New("Building frontend...")
buildFESpinner.SetSpinSpeed(50)
buildFESpinner.Start()
} else {
println("Building frontend...")
}
err := NewProgramHelper(projectOptions.Verbose).RunCommand(projectOptions.FrontEnd.Build)
func BuildFrontend(buildCommand string) error {
buildFESpinner := spinner.New("Building frontend...")
buildFESpinner.SetSpinSpeed(50)
buildFESpinner.Start()
err := NewProgramHelper().RunCommand(buildCommand)
if err != nil {
if buildFESpinner != nil {
buildFESpinner.Error()
}
buildFESpinner.Error()
return err
}
if buildFESpinner != nil {
buildFESpinner.Success()
buildFESpinner.Success()
return nil
}
// CheckMewn checks if mewn is installed and if not, attempts to fetch it
func CheckMewn() (err error) {
programHelper := NewProgramHelper()
if !programHelper.IsInstalled("mewn") {
buildSpinner := spinner.New()
buildSpinner.SetSpinSpeed(50)
buildSpinner.Start("Installing Mewn asset packer...")
err := programHelper.InstallGoPackage("github.com/leaanthony/mewn/cmd/mewn")
if err != nil {
buildSpinner.Error()
return err
}
buildSpinner.Success()
}
return nil
}
// CheckWindres checks if Windres is installed and if not, aborts
func CheckWindres() (err error) {
if runtime.GOOS != "windows" { // FIXME: Handle windows cross-compile for windows!
if runtime.GOOS != "windows" {
return nil
}
programHelper := NewProgramHelper()
@@ -338,15 +194,6 @@ func CheckWindres() (err error) {
return nil
}
// CheckIfInstalled returns if application is installed
func CheckIfInstalled(application string) (err error) {
programHelper := NewProgramHelper()
if !programHelper.IsInstalled(application) {
return fmt.Errorf("%s not installed. Ensure you have installed %s correctly", application, application)
}
return nil
}
// InstallFrontendDeps attempts to install the frontend dependencies based on the given options
func InstallFrontendDeps(projectDir string, projectOptions *ProjectOptions, forceRebuild bool, caller string) error {
@@ -357,14 +204,9 @@ func InstallFrontendDeps(projectDir string, projectOptions *ProjectOptions, forc
}
// Check if frontend deps have been updated
var feSpinner *spinner.Spinner
if !projectOptions.Verbose {
feSpinner = spinner.New("Ensuring frontend dependencies are up to date (This may take a while)")
feSpinner.SetSpinSpeed(50)
feSpinner.Start()
} else {
println("Ensuring frontend dependencies are up to date (This may take a while)")
}
feSpinner := spinner.New("Ensuring frontend dependencies are up to date (This may take a while)")
feSpinner.SetSpinSpeed(50)
feSpinner.Start()
requiresNPMInstall := true
@@ -377,15 +219,6 @@ func InstallFrontendDeps(projectDir string, projectOptions *ProjectOptions, forc
const md5sumFile = "package.json.md5"
// If node_modules does not exist, force a rebuild.
nodeModulesPath, err := filepath.Abs(filepath.Join(".", "node_modules"))
if err != nil {
return err
}
if !fs.DirExists(nodeModulesPath) {
forceRebuild = true
}
// If we aren't forcing the install and the md5sum file exists
if !forceRebuild && fs.FileExists(md5sumFile) {
// Yes - read contents
@@ -396,11 +229,7 @@ func InstallFrontendDeps(projectDir string, projectOptions *ProjectOptions, forc
if savedMD5sum == packageJSONMD5 {
// Same - no need for reinstall
requiresNPMInstall = false
if feSpinner != nil {
feSpinner.Success("Skipped frontend dependencies (-f to force rebuild)")
} else {
println("Skipped frontend dependencies (-f to force rebuild)")
}
feSpinner.Success("Skipped frontend dependencies (-f to force rebuild)")
}
}
}
@@ -409,36 +238,25 @@ func InstallFrontendDeps(projectDir string, projectOptions *ProjectOptions, forc
// Different? Build
if requiresNPMInstall || forceRebuild {
// Install dependencies
err = NewProgramHelper(projectOptions.Verbose).RunCommand(projectOptions.FrontEnd.Install)
err = NewProgramHelper().RunCommand(projectOptions.FrontEnd.Install)
if err != nil {
if feSpinner != nil {
feSpinner.Error()
}
feSpinner.Error()
return err
}
if feSpinner != nil {
feSpinner.Success()
}
feSpinner.Success()
// Update md5sum file
err := os.WriteFile(md5sumFile, []byte(packageJSONMD5), 0644)
if err != nil {
return err
}
ioutil.WriteFile(md5sumFile, []byte(packageJSONMD5), 0644)
}
// Install the runtime
if caller == "build" {
err = InstallProdRuntime(projectDir, projectOptions)
} else {
err = InstallBridge(projectDir, projectOptions)
}
// Install the bridge library
err = InstallBridge(caller, projectDir, projectOptions)
if err != nil {
return err
}
// Build frontend
err = BuildFrontend(projectOptions)
err = BuildFrontend(projectOptions.FrontEnd.Build)
if err != nil {
return err
}
@@ -446,17 +264,21 @@ func InstallFrontendDeps(projectDir string, projectOptions *ProjectOptions, forc
}
// InstallBridge installs the relevant bridge javascript library
func InstallBridge(projectDir string, projectOptions *ProjectOptions) error {
bridgeFileTarget := filepath.Join(projectDir, projectOptions.FrontEnd.Dir, "node_modules", "@wailsapp", "runtime", "init.js")
err := fs.CreateFile(bridgeFileTarget, wailsruntime.BridgeJS)
return err
}
func InstallBridge(caller string, projectDir string, projectOptions *ProjectOptions) error {
bridgeFile := "wailsbridge.prod.js"
if caller == "serve" {
bridgeFile = "wailsbridge.js"
}
// InstallProdRuntime installs the production runtime
func InstallProdRuntime(projectDir string, projectOptions *ProjectOptions) error {
bridgeFileTarget := filepath.Join(projectDir, projectOptions.FrontEnd.Dir, "node_modules", "@wailsapp", "runtime", "init.js")
err := fs.CreateFile(bridgeFileTarget, wailsruntime.InitJS)
return err
// Copy bridge to project
bridgeAssets := mewn.Group("../wailsruntimeassets/bridge/")
bridgeFileData := bridgeAssets.Bytes(bridgeFile)
bridgeFileTarget := filepath.Join(projectDir, projectOptions.FrontEnd.Dir, projectOptions.FrontEnd.Bridge, "wailsbridge.js")
err := fs.CreateFile(bridgeFileTarget, bridgeFileData)
if err != nil {
return err
}
return nil
}
// ServeProject attempts to serve up the current project so that it may be connected to
@@ -464,33 +286,15 @@ func InstallProdRuntime(projectDir string, projectOptions *ProjectOptions) error
func ServeProject(projectOptions *ProjectOptions, logger *Logger) error {
go func() {
time.Sleep(2 * time.Second)
if projectOptions.Platform == "windows" {
logger.Yellow("*** Please note: Windows builds use mshtml which is only compatible with IE11. We strongly recommend only using IE11 when running 'wails serve'! For more information, please read https://wails.app/guides/windows/ ***")
}
logger.Green(">>>>> To connect, you will need to run '" + projectOptions.FrontEnd.Serve + "' in the '" + projectOptions.FrontEnd.Dir + "' directory <<<<<")
}()
location, err := filepath.Abs(filepath.Join("build", projectOptions.BinaryName))
location, err := filepath.Abs(projectOptions.BinaryName)
if err != nil {
return err
}
logger.Yellow("Serving Application: " + location)
var args []string
if len(os.Args) > 2 {
foundArgSep := false
for index, arg := range os.Args[2:] {
if arg == "--" {
foundArgSep = true
continue
}
if foundArgSep {
args = os.Args[index:]
break
}
}
logger.Yellow("Passing arguments: %+v", args)
}
cmd := exec.Command(location, args...)
cmd := exec.Command(location)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Run()
@@ -500,43 +304,3 @@ func ServeProject(projectOptions *ProjectOptions, logger *Logger) error {
return nil
}
func ldFlags(po *ProjectOptions, buildMode string) string {
// Setup ld flags
ldflags := "-w -s "
if buildMode == BuildModeDebug {
ldflags = ""
}
// Add windows flags
if po.Platform == "windows" && buildMode == BuildModeProd {
ldflags += "-H windowsgui "
}
if po.UseFirebug {
ldflags += "-X github.com/wailsapp/wails/lib/renderer.UseFirebug=true "
}
ldflags += "-X github.com/wailsapp/wails.BuildMode=" + buildMode
// Add additional ldflags passed in via the `ldflags` cli flag
if len(po.LdFlags) > 0 {
ldflags += " " + po.LdFlags
}
// If we wish to generate typescript
if po.typescriptDefsFilename != "" {
cwd, err := os.Getwd()
if err == nil {
filename := filepath.Join(cwd, po.FrontEnd.Dir, po.typescriptDefsFilename)
ldflags += " -X github.com/wailsapp/wails/lib/binding.typescriptDefinitionFilename=" + filename
}
}
return ldflags
}
func getGitConfigValue(key string) (string, error) {
output, err := exec.Command("git", "config", "--get", "--null", key).Output()
// When using --null git appends a null character (\u0000) to the command output
return strings.TrimRight(string(output), "\u0000"), err
}

View File

@@ -2,12 +2,10 @@ package cmd
import (
"fmt"
"net/url"
"io/ioutil"
"os"
"runtime"
"regexp"
"strings"
"github.com/pkg/browser"
)
// LinuxDistribution is of type int
@@ -16,207 +14,78 @@ type LinuxDistribution int
const (
// Unknown is the catch-all distro
Unknown LinuxDistribution = iota
// Debian distribution
Debian
// Ubuntu distribution
Ubuntu
// Arch linux distribution
Arch
// CentOS linux distribution
CentOS
// Fedora linux distribution
Fedora
// Gentoo distribution
Gentoo
// Zorin distribution
Zorin
// Parrot distribution
Parrot
// Linuxmint distribution
Linuxmint
// VoidLinux distribution
VoidLinux
// Elementary distribution
Elementary
// Kali distribution
Kali
// Neon distribution
Neon
// ArcoLinux distribution
ArcoLinux
// Manjaro distribution
Manjaro
// ManjaroARM distribution
ManjaroARM
// Deepin distribution
Deepin
// Raspbian distribution
Raspbian
// Tumbleweed (OpenSUSE) distribution
Tumbleweed
// Leap (OpenSUSE) distribution
Leap
// ArchLabs distribution
ArchLabs
// PopOS distribution
PopOS
// Solus distribution
Solus
// Ctlos Linux distribution
Ctlos
// EndeavourOS linux distribution
EndeavourOS
// Crux linux distribution
Crux
// RHEL distribution
RHEL
// NixOS distribution
NixOS
// Artix linux distribution
ArtixLinux
//Uos distribution
Uos
// RedHat linux distribution
RedHat
)
// DistroInfo contains all the information relating to a linux distribution
type DistroInfo struct {
Distribution LinuxDistribution
Name string
ID string
Description string
Release string
Distribution LinuxDistribution
Description string
Release string
Codename string
DistributorID string
}
// GetLinuxDistroInfo returns information about the running linux distribution
func GetLinuxDistroInfo() *DistroInfo {
result := &DistroInfo{
Distribution: Unknown,
ID: "unknown",
Name: "Unknown",
}
_, err := os.Stat("/etc/os-release")
if !os.IsNotExist(err) {
osRelease, _ := os.ReadFile("/etc/os-release")
result = parseOsRelease(string(osRelease))
}
return result
}
// parseOsRelease parses the given os-release data and returns
// a DistroInfo struct with the details
func parseOsRelease(osRelease string) *DistroInfo {
result := &DistroInfo{Distribution: Unknown}
// Default value
osID := "unknown"
osNAME := "Unknown"
version := ""
// Split into lines
lines := strings.Split(osRelease, "\n")
// Iterate lines
for _, line := range lines {
// Split each line by the equals char
splitLine := strings.SplitN(line, "=", 2)
// Check we have
if len(splitLine) != 2 {
continue
}
switch splitLine[0] {
case "ID":
osID = strings.ToLower(strings.Trim(splitLine[1], "\""))
case "NAME":
osNAME = strings.Trim(splitLine[1], "\"")
case "VERSION_ID":
version = strings.Trim(splitLine[1], "\"")
}
}
// Check distro name against list of distros
switch osID {
case "fedora":
result.Distribution = Fedora
case "centos":
result.Distribution = CentOS
case "rhel":
result.Distribution = RHEL
case "arch":
result.Distribution = Arch
case "archlabs":
result.Distribution = ArchLabs
case "ctlos":
result.Distribution = Ctlos
case "debian":
result.Distribution = Debian
case "ubuntu":
result.Distribution = Ubuntu
case "gentoo":
result.Distribution = Gentoo
case "zorin":
result.Distribution = Zorin
case "parrot":
result.Distribution = Parrot
case "linuxmint":
result.Distribution = Linuxmint
case "void":
result.Distribution = VoidLinux
case "elementary":
result.Distribution = Elementary
case "kali":
result.Distribution = Kali
case "neon":
result.Distribution = Neon
case "arcolinux":
result.Distribution = ArcoLinux
case "manjaro":
result.Distribution = Manjaro
case "manjaro-arm":
result.Distribution = ManjaroARM
case "deepin":
result.Distribution = Deepin
case "raspbian":
result.Distribution = Raspbian
case "opensuse-tumbleweed":
result.Distribution = Tumbleweed
case "opensuse-leap":
result.Distribution = Leap
case "pop":
result.Distribution = PopOS
case "solus":
result.Distribution = Solus
case "endeavouros":
result.Distribution = EndeavourOS
case "crux":
result.Distribution = Crux
case "nixos":
result.Distribution = NixOS
case "artix":
result.Distribution = ArtixLinux
case "uos":
result.Distribution = Uos
default:
result.Distribution = Unknown
}
result.Name = osNAME
result.ID = osID
result.Release = version
return result
}
// CheckPkgInstalled is all functions that use local programs to see if a package is installed
type CheckPkgInstalled func(string) (bool, error)
// EqueryInstalled uses equery to see if a package is installed
func EqueryInstalled(packageName string) (bool, error) {
program := NewProgramHelper()
equery := program.FindProgram("equery")
if equery == nil {
return false, fmt.Errorf("cannont check dependencies: equery not found")
// Does lsb_release exist?
lsbRelease := program.FindProgram("lsb_release")
if lsbRelease != nil {
stdout, _, _, err := lsbRelease.Run("-a")
if err != nil {
return result
}
for _, line := range strings.Split(stdout, "\n") {
if strings.Contains(line, ":") {
// Iterate lines a
details := strings.Split(line, ":")
key := strings.TrimSpace(details[0])
value := strings.TrimSpace(details[1])
switch key {
case "Distributor ID":
result.DistributorID = value
switch value {
case "Ubuntu":
result.Distribution = Ubuntu
case "Arch":
result.Distribution = Arch
case "ManjaroLinux":
result.Distribution = Arch
}
case "Description":
result.Description = value
case "Release":
result.Release = value
case "Codename":
result.Codename = value
}
}
}
// check if /etc/os-release exists
} else if _, err := os.Stat("/etc/os-release"); !os.IsNotExist(err) {
// read /etc/os-release
osRelease, _ := ioutil.ReadFile("/etc/os-release")
// compile a regex to find NAME=distro
re := regexp.MustCompile(`^NAME=(.*)\n`)
// extract the distro name
osName := string(re.FindSubmatch(osRelease)[1])
// Check distro name against list of RedHat distros
if osName == "Fedora" || osName == "CentOS" {
//if it matches set result.Distribution to RedHat
result.Distribution = RedHat
}
}
_, _, exitCode, _ := equery.Run("l", packageName)
return exitCode == 0, nil
return result
}
// DpkgInstalled uses dpkg to see if a package is installed
@@ -230,17 +99,6 @@ func DpkgInstalled(packageName string) (bool, error) {
return exitCode == 0, nil
}
// EOpkgInstalled uses dpkg to see if a package is installed
func EOpkgInstalled(packageName string) (bool, error) {
program := NewProgramHelper()
eopkg := program.FindProgram("eopkg")
if eopkg == nil {
return false, fmt.Errorf("cannot check dependencies: eopkg not found")
}
stdout, _, _, _ := eopkg.Run("info", packageName)
return strings.HasPrefix(stdout, "Installed"), nil
}
// PacmanInstalled uses pacman to see if a package is installed.
func PacmanInstalled(packageName string) (bool, error) {
program := NewProgramHelper()
@@ -252,17 +110,6 @@ func PacmanInstalled(packageName string) (bool, error) {
return exitCode == 0, nil
}
// XbpsInstalled uses pacman to see if a package is installed.
func XbpsInstalled(packageName string) (bool, error) {
program := NewProgramHelper()
xbpsQuery := program.FindProgram("xbps-query")
if xbpsQuery == nil {
return false, fmt.Errorf("cannot check dependencies: xbps-query not found")
}
_, _, exitCode, _ := xbpsQuery.Run("-S", packageName)
return exitCode == 0, nil
}
// RpmInstalled uses rpm to see if a package is installed
func RpmInstalled(packageName string) (bool, error) {
program := NewProgramHelper()
@@ -273,71 +120,3 @@ func RpmInstalled(packageName string) (bool, error) {
_, _, exitCode, _ := rpm.Run("--query", packageName)
return exitCode == 0, nil
}
// PrtGetInstalled uses prt-get to see if a package is installed
func PrtGetInstalled(packageName string) (bool, error) {
program := NewProgramHelper()
prtget := program.FindProgram("prt-get")
if prtget == nil {
return false, fmt.Errorf("cannot check dependencies: prt-get not found")
}
_, _, exitCode, _ := prtget.Run("isinst", packageName)
return exitCode == 0, nil
}
// NixEnvInstalled uses nix-env to see if a package is installed
func NixEnvInstalled(packageName string) (bool, error) {
program := NewProgramHelper()
nixEnv := program.FindProgram("nix-env")
if nixEnv == nil {
return false, fmt.Errorf("cannot check dependencies: nix-env not found")
}
packageName = strings.ReplaceAll(packageName, "+", `\+`)
_, _, exitCode, _ := nixEnv.Run("-q", packageName)
return exitCode == 0, nil
}
// RequestSupportForDistribution promts the user to submit a request to support their
// currently unsupported distribution
func RequestSupportForDistribution(distroInfo *DistroInfo) error {
var logger = NewLogger()
defaultError := fmt.Errorf("unable to check libraries on distribution '%s'", distroInfo.Name)
logger.Yellow("Distribution '%s' is not currently supported, but we would love to!", distroInfo.Name)
q := fmt.Sprintf("Would you like to submit a request to support distribution '%s'?", distroInfo.Name)
result := Prompt(q, "yes")
if strings.ToLower(result) != "yes" {
return defaultError
}
title := fmt.Sprintf("Support Distribution '%s'", distroInfo.Name)
var str strings.Builder
gomodule, exists := os.LookupEnv("GO111MODULE")
if !exists {
gomodule = "(Not Set)"
}
str.WriteString("\n| Name | Value |\n| ----- | ----- |\n")
str.WriteString(fmt.Sprintf("| Wails Version | %s |\n", Version))
str.WriteString(fmt.Sprintf("| Go Version | %s |\n", runtime.Version()))
str.WriteString(fmt.Sprintf("| Platform | %s |\n", runtime.GOOS))
str.WriteString(fmt.Sprintf("| Arch | %s |\n", runtime.GOARCH))
str.WriteString(fmt.Sprintf("| GO111MODULE | %s |\n", gomodule))
str.WriteString(fmt.Sprintf("| Distribution ID | %s |\n", distroInfo.ID))
str.WriteString(fmt.Sprintf("| Distribution Name | %s |\n", distroInfo.Name))
str.WriteString(fmt.Sprintf("| Distribution Version | %s |\n", distroInfo.Release))
body := fmt.Sprintf("**Description**\nDistribution '%s' is currently unsupported.\n\n**Further Information**\n\n%s\n\n*Please add any extra information here, EG: libraries that are needed to make the distribution work, or commands to install them*", distroInfo.ID, str.String())
fullURL := "https://github.com/wailsapp/wails/issues/new?"
params := "title=" + title + "&body=" + body
fmt.Println("Opening browser to file request.")
browser.OpenURL(fullURL + url.PathEscape(params))
result = Prompt("We have a guide for adding support for your distribution. Would you like to view it?", "yes")
if strings.ToLower(result) == "yes" {
browser.OpenURL("https://wails.app/guides/distro/")
}
return nil
}

View File

@@ -1,46 +0,0 @@
package cmd
import "testing"
func TestUbuntuDetection(t *testing.T) {
osrelease := `
NAME="Ubuntu"
VERSION="18.04.2 LTS (Bionic Beaver)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 18.04.2 LTS"
VERSION_ID="18.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=bionic
UBUNTU_CODENAME=bionic
`
result := parseOsRelease(osrelease)
if result.Distribution != Ubuntu {
t.Errorf("expected 'Ubuntu' ID but got '%d'", result.Distribution)
}
}
func TestTumbleweedDetection(t *testing.T) {
osrelease := `
NAME="openSUSE Tumbleweed"
# VERSION="20200414"
ID="opensuse-tumbleweed"
ID_LIKE="opensuse suse"
VERSION_ID="20200414"
PRETTY_NAME="openSUSE Tumbleweed"
ANSI_COLOR="0;32"
CPE_NAME="cpe:/o:opensuse:tumbleweed:20200414"
BUG_REPORT_URL="https://bugs.opensuse.org"
HOME_URL="https://www.opensuse.org/"
LOGO="distributor-logo"
`
result := parseOsRelease(osrelease)
if result.Distribution != Tumbleweed {
t.Errorf("expected 'Tumbleweed' ID but got '%d'", result.Distribution)
}
}

View File

@@ -1,93 +0,0 @@
package cmd
import (
_ "embed"
"log"
"gopkg.in/yaml.v3"
)
//go:embed linuxdb.yaml
var LinuxDBYaml []byte
// LinuxDB is the database for linux distribution data.
type LinuxDB struct {
Distributions map[string]*Distribution `yaml:"distributions"`
}
// Distribution holds the os-release ID and a map of releases.
type Distribution struct {
ID string `yaml:"id"`
Releases map[string]*Release `yaml:"releases"`
}
// GetRelease attempts to return the specific Release information
// for the given release name. If there is no specific match, the
// default release data is returned.
func (d *Distribution) GetRelease(version string) *Release {
result := d.Releases[version]
if result == nil {
result = d.Releases["default"]
}
return result
}
// Release holds the name and version of the release as given by
// os-release. Programs is a slice of dependant programs required
// to be present on the local installation for Wails to function.
// Libraries is a slice of libraries that must be present for Wails
// applications to compile.
type Release struct {
Name string `yaml:"name"`
Version string `yaml:"version"`
GccVersionCommand string `yaml:"gccversioncommand"`
Programs []*Prerequisite `yaml:"programs"`
Libraries []*Prerequisite `yaml:"libraries"`
}
// Prerequisite is a simple struct containing a program/library name
// plus the distribution specific help text indicating how to install
// it.
type Prerequisite struct {
Name string `yaml:"name"`
Help string `yaml:"help,omitempty"`
}
// Load will load the given filename from disk and attempt to
// import the data into the LinuxDB.
func (l *LinuxDB) Load(filename string) error {
if fs.FileExists(filename) {
data, err := fs.LoadAsBytes(filename)
if err != nil {
return err
}
return l.ImportData(data)
}
return nil
}
// ImportData will unmarshal the given YAML formatted data
// into the LinuxDB
func (l *LinuxDB) ImportData(data []byte) error {
return yaml.Unmarshal(data, l)
}
// GetDistro returns the Distribution information for the
// given distribution name. If the distribution is not supported,
// nil is returned.
func (l *LinuxDB) GetDistro(distro string) *Distribution {
return l.Distributions[distro]
}
// NewLinuxDB creates a new LinuxDB instance from the bundled
// linuxdb.yaml file.
func NewLinuxDB() *LinuxDB {
result := LinuxDB{
Distributions: make(map[string]*Distribution),
}
err := result.ImportData(LinuxDBYaml)
if err != nil {
log.Fatal(err)
}
return &result
}

View File

@@ -1,384 +0,0 @@
---
distributions:
debian:
id: debian
releases:
default:
name: Debian
version: default
gccversioncommand: &gccdumpversion -dumpversion
programs: &debiandefaultprograms
- name: gcc
help: Please install with `sudo apt-get install build-essential` and try again
- name: pkg-config
help: Please install with `sudo apt-get install pkg-config` and try again
- name: npm
help: Please install with `curl -sL https://deb.nodesource.com/setup_12.x | sudo bash - && sudo apt-get install -y nodejs` and try again
libraries: &debiandefaultlibraries
- name: libgtk-3-dev
help: Please install with `sudo apt-get install libgtk-3-dev` and try again
- name: libwebkit2gtk-4.0-dev
help: Please install with `sudo apt-get install libwebkit2gtk-4.0-dev` and try again
ubuntu:
id: ubuntu
releases:
default:
version: default
name: Ubuntu
gccversioncommand: &gccdumpfullversion -dumpfullversion
programs: *debiandefaultprograms
libraries: *debiandefaultlibraries
pop:
id: pop
releases:
default:
version: default
name: Pop!_OS
gccversioncommand: &gccdumpfullversion -dumpfullversion
programs: *debiandefaultprograms
libraries: *debiandefaultlibraries
kali:
id: kali
releases:
default:
version: default
name: Kali GNU/Linux
gccversioncommand: *gccdumpfullversion
programs: *debiandefaultprograms
libraries: *debiandefaultlibraries
parrot:
id: parrot
releases:
default:
version: default
name: Parrot GNU/Linux
gccversioncommand: *gccdumpversion
programs: *debiandefaultprograms
libraries: *debiandefaultlibraries
zorin:
id: zorin
releases:
default:
version: default
name: Zorin
gccversioncommand: *gccdumpversion
programs: *debiandefaultprograms
libraries: *debiandefaultlibraries
linuxmint:
id: linuxmint
releases:
default:
version: default
name: Linux Mint
gccversioncommand: *gccdumpversion
programs: *debiandefaultprograms
libraries: *debiandefaultlibraries
elementary:
id: elementary
releases:
default:
version: default
name: elementary OS
gccversioncommand: *gccdumpfullversion
programs: *debiandefaultprograms
libraries: *debiandefaultlibraries
neon:
id: neon
releases:
default:
version: default
name: KDE neon
gccversioncommand: *gccdumpfullversion
programs: *debiandefaultprograms
libraries: *debiandefaultlibraries
deepin:
id: deepin
releases:
default:
version: default
name: Deepin
gccversioncommand: *gccdumpfullversion
programs: *debiandefaultprograms
libraries: *debiandefaultlibraries
uos:
id: uos
releases:
default:
version: default
name: Uos
gccversioncommand: *gccdumpfullversion
programs: *debiandefaultprograms
libraries: *debiandefaultlibraries
void:
id: void
releases:
default:
version: default
name: VoidLinux
gccversioncommand: *gccdumpversion
programs:
- name: gcc
help: Please install with `xbps-install base-devel` and try again
- name: pkg-config
help: Please install with `xbps-install pkg-config` and try again
- name: npm
help: Please install with `xbps-install nodejs` and try again
libraries:
- name: gtk+3-devel
help: Please install with `xbps-install gtk+3-devel` and try again
- name: webkit2gtk-devel
help: Please install with `xbps-install webkit2gtk-devel` and try again
centos:
id: centos
releases:
default:
version: default
name: CentOS Linux
gccversioncommand: *gccdumpversion
programs:
- name: gcc
help: Please install with `sudo yum install gcc-c++ make` and try again
- name: pkg-config
help: Please install with `sudo yum install pkgconf-pkg-config` and try again
- name: npm
help: Please install with `sudo yum install epel-release && sudo yum install nodejs` and try again
libraries:
- name: gtk3-devel
help: Please install with `sudo yum install gtk3-devel` and try again
- name: webkitgtk3-devel
help: Please install with `sudo yum install webkitgtk3-devel` and try again
rhel:
id: rhel
releases:
default:
version: default
name: Red Hat Enterprise Linux
gccversioncommand: *gccdumpversion
programs:
- name: gcc
help: Please install with `sudo yum install gcc-c++ make` and try again
- name: pkg-config
help: Please install with `sudo yum install pkgconf-pkg-config` and try again
- name: npm
help: Please install with `sudo yum install epel-release && sudo yum install nodejs` and try again
libraries:
- name: gtk3-devel
help: Please install with `sudo yum install gtk3-devel` and try again
- name: webkitgtk3-devel
help: Please install with `sudo yum install webkitgtk3-devel` and try again
fedora:
id: fedora
releases:
default:
version: default
name: Fedora
gccversioncommand: *gccdumpfullversion
programs:
- name: gcc
help: Please install with `sudo yum install gcc-c++ make` and try again
- name: pkg-config
help: Please install with `sudo yum install pkgconf-pkg-config` and try again
- name: npm
help: Please install `sudo yum install nodejs` and try again
libraries:
- name: gtk3-devel
help: Please install with `sudo yum install gtk3-devel` and try again
- name: webkit2gtk3-devel
help: Please install with `sudo yum install webkit2gtk3-devel` and try again
arch:
id: arch
releases:
default:
version: default
name: Arch Linux
gccversioncommand: *gccdumpversion
programs: &archdefaultprograms
- name: gcc
help: Please install with `sudo pacman -S gcc` and try again
- name: pkgconf
help: Please install with `sudo pacman -S pkgconf` and try again
- name: npm
help: Please install with `sudo pacman -S npm` and try again
libraries: &archdefaultlibraries
- name: gtk3
help: Please install with `sudo pacman -S gtk3` and try again
- name: webkit2gtk
help: Please install with `sudo pacman -S webkit2gtk` and try again
arcolinux:
id: arcolinux
releases:
default:
version: default
name: ArcoLinux
gccversioncommand: *gccdumpversion
programs: *archdefaultprograms
libraries: *archdefaultlibraries
archlabs:
id: archlabs
releases:
default:
version: default
name: ArchLabs
gccversioncommand: *gccdumpversion
programs: *archdefaultprograms
libraries: *archdefaultlibraries
artix:
id: artix
releases:
default:
version: default
name: Artix Linux
gccversioncommand: *gccdumpversion
programs: *archdefaultprograms
libraries: *archdefaultlibraries
ctlos:
id: ctlos
releases:
default:
version: default
name: Ctlos Linux
gccversioncommand: *gccdumpversion
programs: *archdefaultprograms
libraries: *archdefaultlibraries
endeavouros:
id: endeavouros
releases:
default:
version: default
name: EndeavourOS
gccversioncommand: *gccdumpversion
programs: *archdefaultprograms
libraries: *archdefaultlibraries
manjaro:
id: manjaro
releases:
default:
version: default
name: Manjaro Linux
gccversioncommand: *gccdumpversion
programs: *archdefaultprograms
libraries: *archdefaultlibraries
manjaro-arm:
id: manjaro-arm
releases:
default:
version: default
name: Manjaro-ARM
gccversioncommand: *gccdumpversion
programs: *archdefaultprograms
libraries: *archdefaultlibraries
gentoo:
id: gentoo
releases:
default:
version: default
name: Gentoo
gccversioncommand: *gccdumpversion
programs:
- name: gcc
help: Please install using your system's package manager
- name: pkg-config
help: Please install using your system's package manager
- name: npm
help: Please install using your system's package manager
libraries:
- name: gtk+:3
help: Please install with `sudo emerge gtk+:3` and try again
- name: webkit-gtk
help: Please install with `sudo emerge webkit-gtk` and try again
raspbian:
id: raspbian
releases:
default:
version: default
name: Raspbian
gccversioncommand: *gccdumpfullversion
programs: *debiandefaultprograms
libraries: *debiandefaultlibraries
solus:
id: solus
releases:
default:
version: default
name: Solus
gccversioncommand: *gccdumpfullversion
programs: &solusdefaultprograms
- name: gcc
help: Please install with `sudo eopkg it -c system.devel` and try again
- name: pkg-config
help: Please install with `sudo eopkg it -c system.devel` and try again
- name: npm
help: Please install with `sudo eopkg it nodejs` and try again
libraries: &solusdefaultlibraries
- name: libgtk-3-devel
help: Please install with `sudo eopkg it libgtk-3-devel` and try again
- name: libwebkit-gtk-devel
help: Please install with `sudo eopkg it libwebkit-gtk-devel` and try again
opensuse-tumbleweed:
id: opensuse-tumbleweed
releases:
default:
version: default
name: openSUSE Tumbleweed
gccversioncommand: *gccdumpfullversion
programs: &opensusedefaultprograms
- name: gcc
help: Please install with `sudo zypper in gcc-c++` and try again
- name: pkg-config
help: Please install with `sudo zypper in pkgconf-pkg-config` and try again
- name: npm
help: Please install `sudo zypper in nodejs` and try again
libraries: &opensusedefaultlibraries
- name: gtk3-devel
help: Please install with `sudo zypper in gtk3-devel` and try again
- name: webkit2gtk3-devel
help: Please install with `sudo zypper in webkit2gtk3-devel` and try again
opensuse-leap:
id: opensuse-leap
releases:
default:
version: default
name: openSUSE Leap
gccversioncommand: *gccdumpfullversion
programs: *opensusedefaultprograms
libraries: *opensusedefaultlibraries
crux:
id: crux
releases:
default:
version: default
name: Crux Linux
gccversioncommand: *gccdumpversion
programs:
- name: gcc
help: Please install with `sudo prt-get depinst gcc-c++ make` and try again
- name: pkg-config
help: Please install with `sudo prt-get depinst pkg-config` and try again
- name: npm
help: Please install with `sudo prt-get depinst nodejs` and try again
libraries:
- name: gtk3
help: Please install with `sudo prt-get depinst gtk3` and try again
- name: webkitgtk
help: Please install with `sudo prt-get depinst webkitgtk` and try again
nixos:
id: nixos
releases:
default:
version: default
name: NixOS
gccversioncommand: *gccdumpversion
programs:
- name: gcc
help: Please install with `nix-env -iA nixos.gcc`
- name: pkg-config
help: Please install with `nix-env -iA nixos.pkg-config`
- name: npm
help: Please install with `nix-env -iA nixos.nodejs`
libraries:
- name: gtk+3
help: Please install with `nix-env -iA nixos.gtk3`
- name: webkitgtk
help: Please install with `nix-env -iA nixos.nodePackages.webkitgtk`

View File

@@ -1,81 +0,0 @@
package cmd
import "testing"
func TestNewLinuxDB(t *testing.T) {
_ = NewLinuxDB()
}
func TestKnownDistro(t *testing.T) {
var linuxDB = NewLinuxDB()
result := linuxDB.GetDistro("ubuntu")
if result == nil {
t.Error("Cannot get distro 'ubuntu'")
}
}
func TestUnknownDistro(t *testing.T) {
var linuxDB = NewLinuxDB()
result := linuxDB.GetDistro("unknown")
if result != nil {
t.Error("Should get nil for distribution 'unknown'")
}
}
func TestDefaultRelease(t *testing.T) {
var linuxDB = NewLinuxDB()
result := linuxDB.GetDistro("ubuntu")
if result == nil {
t.Error("Cannot get distro 'ubuntu'")
}
release := result.GetRelease("default")
if release == nil {
t.Error("Cannot get release 'default' for distro 'ubuntu'")
}
}
func TestUnknownRelease(t *testing.T) {
var linuxDB = NewLinuxDB()
result := linuxDB.GetDistro("ubuntu")
if result == nil {
t.Error("Cannot get distro 'ubuntu'")
}
release := result.GetRelease("16.04")
if release == nil {
t.Error("Failed to get release 'default' for unknown release version '16.04'")
}
if release.Version != "default" {
t.Errorf("Got version '%s' instead of 'default' for unknown release version '16.04'", result.ID)
}
}
func TestGetPrerequisites(t *testing.T) {
var linuxDB = NewLinuxDB()
result := linuxDB.GetDistro("debian")
if result == nil {
t.Error("Cannot get distro 'debian'")
}
release := result.GetRelease("default")
if release == nil {
t.Error("Failed to get release 'default' for unknown release version '16.04'")
}
if release.Version != "default" {
t.Errorf("Got version '%s' instead of 'default' for unknown release version '16.04'", result.ID)
}
if release.Name != "Debian" {
t.Errorf("Got Release Name '%s' instead of 'debian' for unknown release version '16.04'", release.Name)
}
if len(release.Programs) != 3 {
t.Errorf("Expected %d programs for unknown release version '16.04'", len(release.Programs))
}
if len(release.Libraries) != 2 {
t.Errorf("Expected %d libraries for unknown release version '16.04'", len(release.Libraries))
}
}

View File

@@ -1,12 +1,10 @@
package cmd
import (
"bufio"
"bytes"
"encoding/binary"
"fmt"
"image"
"image/png"
"io/ioutil"
"os"
"path"
"path/filepath"
@@ -16,24 +14,21 @@ import (
"time"
"github.com/jackmordaunt/icns"
"golang.org/x/image/draw"
)
// PackageHelper helps with the 'wails package' command
type PackageHelper struct {
platform string
fs *FSHelper
log *Logger
system *SystemHelper
fs *FSHelper
log *Logger
system *SystemHelper
}
// NewPackageHelper creates a new PackageHelper!
func NewPackageHelper(platform string) *PackageHelper {
func NewPackageHelper() *PackageHelper {
return &PackageHelper{
platform: platform,
fs: NewFSHelper(),
log: NewLogger(),
system: NewSystemHelper(),
fs: NewFSHelper(),
log: NewLogger(),
system: NewSystemHelper(),
}
}
@@ -58,111 +53,6 @@ func newPlistData(title, exe, packageID, version, author string) *plistData {
}
}
type windowsIcoHeader struct {
_ uint16
imageType uint16
imageCount uint16
}
type windowsIcoDescriptor struct {
width uint8
height uint8
colours uint8
_ uint8
planes uint16
bpp uint16
size uint32
offset uint32
}
type windowsIcoContainer struct {
Header windowsIcoDescriptor
Data []byte
}
func generateWindowsIcon(pngFilename string, iconfile string) error {
sizes := []int{256, 128, 64, 48, 32, 16}
pngfile, err := os.Open(pngFilename)
if err != nil {
return err
}
defer pngfile.Close()
pngdata, err := png.Decode(pngfile)
if err != nil {
return err
}
icons := []windowsIcoContainer{}
for _, size := range sizes {
rect := image.Rect(0, 0, int(size), int(size))
rawdata := image.NewRGBA(rect)
scale := draw.CatmullRom
scale.Scale(rawdata, rect, pngdata, pngdata.Bounds(), draw.Over, nil)
icondata := new(bytes.Buffer)
writer := bufio.NewWriter(icondata)
err = png.Encode(writer, rawdata)
if err != nil {
return err
}
writer.Flush()
imgSize := size
if imgSize >= 256 {
imgSize = 0
}
data := icondata.Bytes()
icn := windowsIcoContainer{
Header: windowsIcoDescriptor{
width: uint8(imgSize),
height: uint8(imgSize),
planes: 1,
bpp: 32,
size: uint32(len(data)),
},
Data: data,
}
icons = append(icons, icn)
}
outfile, err := os.Create(iconfile)
if err != nil {
return err
}
defer outfile.Close()
ico := windowsIcoHeader{
imageType: 1,
imageCount: uint16(len(sizes)),
}
err = binary.Write(outfile, binary.LittleEndian, ico)
if err != nil {
return err
}
offset := uint32(6 + 16*len(sizes))
for _, icon := range icons {
icon.Header.offset = offset
err = binary.Write(outfile, binary.LittleEndian, icon.Header)
if err != nil {
return err
}
offset += icon.Header.size
}
for _, icon := range icons {
_, err = outfile.Write(icon.Data)
if err != nil {
return err
}
}
return nil
}
func defaultString(val string, defaultVal string) string {
if val != "" {
return val
@@ -173,30 +63,29 @@ func defaultString(val string, defaultVal string) string {
func (b *PackageHelper) getPackageFileBaseDir() string {
// Calculate template base dir
_, filename, _, _ := runtime.Caller(1)
return filepath.Join(path.Dir(filename), "packages", b.platform)
return filepath.Join(path.Dir(filename), "packages", runtime.GOOS)
}
// Package the application into a platform specific package
func (b *PackageHelper) Package(po *ProjectOptions) error {
switch b.platform {
switch runtime.GOOS {
case "darwin":
// Check we have the exe
if !b.fs.FileExists(po.BinaryName) {
return fmt.Errorf("cannot bundle non-existent binary file '%s'. Please build with 'wails build' first", po.BinaryName)
}
return b.packageOSX(po)
case "windows":
return b.PackageWindows(po, true)
return b.PackageWindows(po, false)
case "linux":
return b.packageLinux(po)
return fmt.Errorf("linux is not supported at this time. Please see https://github.com/wailsapp/wails/issues/2")
default:
return fmt.Errorf("platform '%s' not supported for bundling yet", b.platform)
return fmt.Errorf("platform '%s' not supported for bundling yet", runtime.GOOS)
}
}
func (b *PackageHelper) packageLinux(po *ProjectOptions) error {
return nil
}
// Package the application for OSX
func (b *PackageHelper) packageOSX(po *ProjectOptions) error {
build := path.Join(b.fs.Cwd(), "build")
system := NewSystemHelper()
config, err := system.LoadConfig()
@@ -211,68 +100,39 @@ func (b *PackageHelper) packageOSX(po *ProjectOptions) error {
packageID := strings.Join([]string{"wails", name, version}, ".")
plistData := newPlistData(name, exe, packageID, version, author)
appname := po.Name + ".app"
plistFilename := path.Join(build, appname, "Contents", "Info.plist")
customPlist := path.Join(b.fs.Cwd(), "info.plist")
// Check binary exists
source := path.Join(build, exe)
if po.CrossCompile == true {
file, err := b.fs.FindFile(build, "darwin")
if err != nil {
return err
}
source = path.Join(build, file)
}
source := path.Join(b.fs.Cwd(), exe)
if !b.fs.FileExists(source) {
// We need to build!
return fmt.Errorf("Target '%s' not available. Has it been compiled yet?", source)
return fmt.Errorf("Target '%s' not available. Has it been compiled yet?", exe)
}
// Remove the existing package
os.RemoveAll(appname)
// Create directories
exeDir := path.Join(build, appname, "/Contents/MacOS")
exeDir := path.Join(b.fs.Cwd(), appname, "/Contents/MacOS")
b.fs.MkDirs(exeDir, 0755)
resourceDir := path.Join(build, appname, "/Contents/Resources")
resourceDir := path.Join(b.fs.Cwd(), appname, "/Contents/Resources")
b.fs.MkDirs(resourceDir, 0755)
tmpl := template.New("infoPlist")
plistFile := filepath.Join(b.getPackageFileBaseDir(), "info.plist")
infoPlist, err := ioutil.ReadFile(plistFile)
if err != nil {
return err
}
tmpl.Parse(string(infoPlist))
// Do we have a custom plist in the project directory?
if !fs.FileExists(customPlist) {
// No - create a new plist from our defaults
tmpl := template.New("infoPlist")
plistFile := filepath.Join(b.getPackageFileBaseDir(), "info.plist")
infoPlist, err := os.ReadFile(plistFile)
if err != nil {
return err
}
tmpl.Parse(string(infoPlist))
// Write the template to a buffer
var tpl bytes.Buffer
err = tmpl.Execute(&tpl, plistData)
if err != nil {
return err
}
// Save to the package
err = os.WriteFile(plistFilename, tpl.Bytes(), 0644)
if err != nil {
return err
}
// Also write to project directory for customisation
err = os.WriteFile(customPlist, tpl.Bytes(), 0644)
if err != nil {
return err
}
} else {
// Yes - we have a plist. Copy it to the package verbatim
err = fs.CopyFile(customPlist, plistFilename)
if err != nil {
return err
}
// Write the template to a buffer
var tpl bytes.Buffer
err = tmpl.Execute(&tpl, plistData)
if err != nil {
return err
}
filename := path.Join(b.fs.Cwd(), appname, "Contents", "Info.plist")
err = ioutil.WriteFile(filename, tpl.Bytes(), 0644)
if err != nil {
return err
}
// Copy executable
@@ -290,39 +150,22 @@ func (b *PackageHelper) packageOSX(po *ProjectOptions) error {
return err
}
// CleanWindows removes any windows related files found in the directory
func (b *PackageHelper) CleanWindows(po *ProjectOptions) {
pdir := b.fs.Cwd()
basename := strings.TrimSuffix(po.BinaryName, ".exe")
exts := []string{".ico", ".exe.manifest", ".rc", "-res.syso"}
rsrcs := []string{}
for _, ext := range exts {
rsrcs = append(rsrcs, filepath.Join(pdir, basename+ext))
}
b.fs.RemoveFiles(rsrcs, true)
}
// PackageWindows packages the application for windows platforms
func (b *PackageHelper) PackageWindows(po *ProjectOptions, cleanUp bool) error {
outputDir := b.fs.Cwd()
basename := strings.TrimSuffix(po.BinaryName, ".exe")
// Copy default icon if needed
icon, err := b.copyIcon()
if err != nil {
return err
}
// Generate icon from PNG if it doesn't exist
if !fs.FileExists(basename + ".ico") {
err = generateWindowsIcon(icon, basename+".ico")
// Copy icon
tgtIconFile := filepath.Join(b.fs.Cwd(), basename+".ico")
if !b.fs.FileExists(tgtIconFile) {
srcIconfile := filepath.Join(b.getPackageFileBaseDir(), "wails.ico")
err := b.fs.CopyFile(srcIconfile, tgtIconFile)
if err != nil {
return err
}
}
// Copy manifest
tgtManifestFile := filepath.Join(outputDir, basename+".exe.manifest")
tgtManifestFile := filepath.Join(b.fs.Cwd(), basename+".exe.manifest")
if !b.fs.FileExists(tgtManifestFile) {
srcManifestfile := filepath.Join(b.getPackageFileBaseDir(), "wails.exe.manifest")
err := b.fs.CopyFile(srcManifestfile, tgtManifestFile)
@@ -332,52 +175,41 @@ func (b *PackageHelper) PackageWindows(po *ProjectOptions, cleanUp bool) error {
}
// Copy rc file
tgtRCFile := filepath.Join(outputDir, basename+".rc")
tgtRCFile := filepath.Join(b.fs.Cwd(), basename+".rc")
if !b.fs.FileExists(tgtRCFile) {
srcRCfile := filepath.Join(b.getPackageFileBaseDir(), "wails.rc")
rcfilebytes, err := os.ReadFile(srcRCfile)
rcfilebytes, err := ioutil.ReadFile(srcRCfile)
if err != nil {
return err
}
rcfiledata := strings.Replace(string(rcfilebytes), "$NAME$", basename, -1)
err = os.WriteFile(tgtRCFile, []byte(rcfiledata), 0755)
err = ioutil.WriteFile(tgtRCFile, []byte(rcfiledata), 0755)
if err != nil {
return err
}
}
// Build syso
sysofile := filepath.Join(outputDir, basename+"-res.syso")
sysofile := filepath.Join(b.fs.Cwd(), basename+"-res.syso")
windresCommand := []string{"windres", "-o", sysofile, tgtRCFile}
err := NewProgramHelper().RunCommandArray(windresCommand)
if err != nil {
return err
}
// cross-compile
if b.platform != runtime.GOOS {
args := []string{
"docker", "run", "--rm",
"-v", outputDir + ":/build",
"--entrypoint", "/bin/sh",
"wailsapp/xgo:1.16.3",
"-c", "/usr/bin/x86_64-w64-mingw32-windres -o /build/" + basename + "-res.syso /build/" + basename + ".rc",
}
if err := NewProgramHelper().RunCommandArray(args); err != nil {
return err
}
} else {
batfile, err := fs.LocalDir(".")
if err != nil {
return err
}
windresBatFile := filepath.Join(batfile.fullPath, "windres.bat")
windresCommand := []string{windresBatFile, sysofile, tgtRCFile}
err = NewProgramHelper().RunCommandArray(windresCommand)
// clean up
if cleanUp {
filesToDelete := []string{tgtIconFile, tgtManifestFile, tgtRCFile, sysofile}
err := b.fs.RemoveFiles(filesToDelete)
if err != nil {
return err
}
}
return nil
}
func (b *PackageHelper) copyIcon() (string, error) {
func (b *PackageHelper) copyIcon(resourceDir string) (string, error) {
// TODO: Read this from project.json
const appIconFilename = "appicon.png"
@@ -388,11 +220,11 @@ func (b *PackageHelper) copyIcon() (string, error) {
// Install default icon
iconfile := filepath.Join(b.getPackageFileBaseDir(), "icon.png")
iconData, err := os.ReadFile(iconfile)
iconData, err := ioutil.ReadFile(iconfile)
if err != nil {
return "", err
}
err = os.WriteFile(srcIcon, iconData, 0644)
err = ioutil.WriteFile(srcIcon, iconData, 0644)
if err != nil {
return "", err
}
@@ -402,7 +234,7 @@ func (b *PackageHelper) copyIcon() (string, error) {
func (b *PackageHelper) packageIconOSX(resourceDir string) error {
srcIcon, err := b.copyIcon()
srcIcon, err := b.copyIcon(resourceDir)
if err != nil {
return err
}

View File

@@ -1,6 +1,5 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"><dict>
<key>CFBundlePackageType</key><string>APPL</string>
<key>CFBundleName</key><string>{{.Title}}</string>
<key>CFBundleExecutable</key><string>{{.Exe}}</string>
<key>CFBundleIdentifier</key><string>{{.PackageID}}</string>

View File

@@ -1,2 +1,2 @@
100 ICON "$NAME$.ico"
110 24 "$NAME$.exe.manifest"
100 24 "$NAME$.exe.manifest"

View File

@@ -5,6 +5,13 @@ import (
"runtime"
)
// Prerequisite defines a Prerequisite!
type Prerequisite struct {
Name string
Help string
Path string
}
func newPrerequisite(name, help string) *Prerequisite {
return &Prerequisite{Name: name, Help: help}
}
@@ -41,13 +48,17 @@ func getRequiredProgramsOSX() *Prerequisites {
func getRequiredProgramsLinux() *Prerequisites {
result := &Prerequisites{}
distroInfo := GetLinuxDistroInfo()
if distroInfo.Distribution != Unknown {
var linuxDB = NewLinuxDB()
distro := linuxDB.GetDistro(distroInfo.ID)
release := distro.GetRelease(distroInfo.Release)
for _, program := range release.Programs {
result.Add(program)
}
switch distroInfo.Distribution {
case Ubuntu:
result.Add(newPrerequisite("gcc", "Please install with `sudo apt install build-essentials` and try again"))
result.Add(newPrerequisite("pkg-config", "Please install with `sudo apt install pkg-config` and try again"))
result.Add(newPrerequisite("npm", "Please install with `sudo apt install npm` and try again"))
default:
result.Add(newPrerequisite("gcc", "Please install with your system package manager and try again"))
result.Add(newPrerequisite("pkg-config", "Please install with your system package manager and try again"))
result.Add(newPrerequisite("npm", "Please install from https://nodejs.org/en/download/ and try again"))
}
return result
}
@@ -81,15 +92,20 @@ func getRequiredLibrariesOSX() (*Prerequisites, error) {
func getRequiredLibrariesLinux() (*Prerequisites, error) {
result := &Prerequisites{}
// The Linux Distribution DB
distroInfo := GetLinuxDistroInfo()
if distroInfo.Distribution != Unknown {
var linuxDB = NewLinuxDB()
distro := linuxDB.GetDistro(distroInfo.ID)
release := distro.GetRelease(distroInfo.Release)
for _, library := range release.Libraries {
result.Add(library)
}
switch distroInfo.Distribution {
case Ubuntu:
result.Add(newPrerequisite("libgtk-3-dev", "Please install with `sudo apt install libgtk-3-dev` and try again"))
result.Add(newPrerequisite("libwebkit2gtk-4.0-dev", "Please install with `sudo apt install libwebkit2gtk-4.0-dev` and try again"))
case Arch:
result.Add(newPrerequisite("gtk3", "Please install with `sudo pacman -S gtk3` and try again"))
result.Add(newPrerequisite("webkit2gtk", "Please install with `sudo pacman -S webkit2gtk` and try again"))
case RedHat:
result.Add(newPrerequisite("gtk3-devel", "Please install with `sudo yum install gtk3-devel` and try again"))
result.Add(newPrerequisite("webkit2gtk3-devel", "Please install with `sudo yum install webkit2gtk3-devel` and try again"))
default:
result.Add(newPrerequisite("libgtk-3-dev", "Please install with your system package manager and try again"))
result.Add(newPrerequisite("libwebkit2gtk-4.0-dev", "Please install with your system package manager and try again"))
}
return result, nil
}

View File

@@ -3,7 +3,6 @@ package cmd
import (
"bytes"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
@@ -12,22 +11,14 @@ import (
// ProgramHelper - Utility functions around installed applications
type ProgramHelper struct {
shell *ShellHelper
verbose bool
shell *ShellHelper
}
// NewProgramHelper - Creates a new ProgramHelper
func NewProgramHelper(verbose ...bool) *ProgramHelper {
result := &ProgramHelper{
func NewProgramHelper() *ProgramHelper {
return &ProgramHelper{
shell: NewShellHelper(),
}
if len(verbose) > 0 {
result.verbose = verbose[0]
if result.verbose {
result.shell.SetVerbose()
}
}
return result
}
// IsInstalled tries to determine if the given binary name is installed
@@ -38,9 +29,8 @@ func (p *ProgramHelper) IsInstalled(programName string) bool {
// Program - A struct to define an installed application/binary
type Program struct {
Name string `json:"name"`
Path string `json:"path"`
verbose bool
Name string `json:"name"`
Path string `json:"path"`
}
// FindProgram attempts to find the given program on the system.FindProgram
@@ -55,9 +45,8 @@ func (p *ProgramHelper) FindProgram(programName string) *Program {
return nil
}
return &Program{
Name: programName,
Path: path,
verbose: p.verbose,
Name: programName,
Path: path,
}
}
@@ -74,18 +63,12 @@ func (p *Program) Run(vars ...string) (stdout, stderr string, exitCode int, err
return "", "", 1, err
}
cmd := exec.Command(command, vars...)
if !p.verbose {
var stdo, stde bytes.Buffer
cmd.Stdout = &stdo
cmd.Stderr = &stde
err = cmd.Run()
stdout = string(stdo.Bytes())
stderr = string(stde.Bytes())
} else {
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Run()
}
var stdo, stde bytes.Buffer
cmd.Stdout = &stdo
cmd.Stderr = &stde
err = cmd.Run()
stdout = string(stdo.Bytes())
stderr = string(stde.Bytes())
// https://stackoverflow.com/questions/10385551/get-exit-code-go
if err != nil {
@@ -117,19 +100,6 @@ func (p *ProgramHelper) InstallGoPackage(packageName string) error {
return err
}
// InstallNPMPackage installs the given npm package
func (p *ProgramHelper) InstallNPMPackage(packageName string, save bool) error {
args := strings.Split("install "+packageName, " ")
if save {
args = append(args, "--save")
}
_, stderr, err := p.shell.Run("npm", args...)
if err != nil {
fmt.Println(stderr)
}
return err
}
// RunCommand runs the given command
func (p *ProgramHelper) RunCommand(command string) error {
args := strings.Split(command, " ")
@@ -138,25 +108,23 @@ func (p *ProgramHelper) RunCommand(command string) error {
// RunCommandArray runs the command specified in the array
func (p *ProgramHelper) RunCommandArray(args []string, dir ...string) error {
programCommand := args[0]
program := args[0]
// TODO: Run FindProgram here and get the full path to the exe
program, err := exec.LookPath(programCommand)
program, err := exec.LookPath(program)
if err != nil {
fmt.Printf("ERROR: Looks like '%s' isn't installed. Please install and try again.", programCommand)
fmt.Printf("ERROR: Looks like '%s' isn't installed. Please install and try again.", program)
return err
}
args = args[1:]
var stderr string
var stdout string
// fmt.Printf("RunCommandArray = %s %+v\n", program, args)
if len(dir) > 0 {
stdout, stderr, err = p.shell.RunInDirectory(dir[0], program, args...)
_, stderr, err = p.shell.RunInDirectory(dir[0], program, args...)
} else {
stdout, stderr, err = p.shell.Run(program, args...)
_, stderr, err = p.shell.Run(program, args...)
}
if err != nil {
fmt.Println(stderr)
fmt.Println(stdout)
}
return err
}

View File

@@ -3,27 +3,15 @@ package cmd
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"runtime"
"sort"
"strings"
"github.com/leaanthony/slicer"
)
// PackageManager indicates different package managers
type PackageManager int
const (
// UNKNOWN package manager
UNKNOWN PackageManager = iota
// NPM package manager
NPM
// YARN package manager
YARN
)
type author struct {
Name string `json:"name"`
Email string `json:"email"`
@@ -62,6 +50,12 @@ func NewProjectHelper() *ProjectHelper {
// GenerateProject generates a new project using the options given
func (ph *ProjectHelper) GenerateProject(projectOptions *ProjectOptions) error {
// exists := ph.templates.TemplateExists(projectOptions.Template)
// if !exists {
// return fmt.Errorf("template '%s' is invalid", projectOptions.Template)
// }
// Calculate project path
projectPath, err := filepath.Abs(projectOptions.OutputDirectory)
if err != nil {
@@ -96,6 +90,8 @@ func (ph *ProjectHelper) GenerateProject(projectOptions *ProjectOptions) error {
// ph.GenerateWindowsResourceConfig(projectOptions)
// }
ph.log.Yellow("Project '%s' generated in directory '%s'!", projectOptions.Name, projectOptions.OutputDirectory)
ph.log.Yellow("To compile the project, run 'wails build' in the project directory.")
return nil
}
@@ -123,9 +119,9 @@ func (ph *ProjectHelper) NewProjectOptions() *ProjectOptions {
Description: "Enter your project description",
Version: "0.1.0",
BinaryName: "",
system: ph.system,
log: ph.log,
templates: ph.templates,
system: NewSystemHelper(),
log: NewLogger(),
templates: NewTemplateHelper(),
Author: &author{},
}
@@ -141,75 +137,25 @@ func (ph *ProjectHelper) NewProjectOptions() *ProjectOptions {
// ProjectOptions holds all the options available for a project
type ProjectOptions struct {
Name string `json:"name"`
Description string `json:"description"`
Author *author `json:"author,omitempty"`
Version string `json:"version"`
OutputDirectory string `json:"-"`
UseDefaults bool `json:"-"`
Template string `json:"-"`
BinaryName string `json:"binaryname"`
FrontEnd *frontend `json:"frontend,omitempty"`
Tags string `json:"tags"`
NPMProjectName string `json:"-"`
system *SystemHelper
log *Logger
templates *TemplateHelper
selectedTemplate *TemplateDetails
WailsVersion string
typescriptDefsFilename string
Verbose bool `json:"-"`
CrossCompile bool
Platform string
Architecture string
LdFlags string
GoPath string
UseFirebug bool
// Supported platforms
Platforms []string `json:"platforms,omitempty"`
}
// PlatformSupported returns true if the template is supported
// on the current platform
func (po *ProjectOptions) PlatformSupported() bool {
// Default is all platforms supported
if len(po.Platforms) == 0 {
return true
}
// Check that the platform is in the list
platformsSupported := slicer.String(po.Platforms)
return platformsSupported.Contains(runtime.GOOS)
Name string `json:"name"`
Description string `json:"description"`
Author *author `json:"author,omitempty"`
Version string `json:"version"`
OutputDirectory string `json:"-"`
UseDefaults bool `json:"-"`
Template string `json:"-"`
BinaryName string `json:"binaryname"`
FrontEnd *frontend `json:"frontend,omitempty"`
NPMProjectName string `json:"-"`
system *SystemHelper
log *Logger
templates *TemplateHelper
selectedTemplate *TemplateDetails
}
// Defaults sets the default project template
func (po *ProjectOptions) Defaults() {
po.Template = "vuebasic"
po.WailsVersion = Version
}
// SetTypescriptDefsFilename indicates that we want to generate typescript bindings to the given file
func (po *ProjectOptions) SetTypescriptDefsFilename(filename string) {
po.typescriptDefsFilename = filename
}
// GetNPMBinaryName returns the type of package manager used by the project
func (po *ProjectOptions) GetNPMBinaryName() (PackageManager, error) {
if po.FrontEnd == nil {
return UNKNOWN, fmt.Errorf("No frontend specified in project options")
}
if strings.Index(po.FrontEnd.Install, "npm") > -1 {
return NPM, nil
}
if strings.Index(po.FrontEnd.Install, "yarn") > -1 {
return YARN, nil
}
return UNKNOWN, nil
}
// PromptForInputs asks the user to input project details
@@ -227,56 +173,19 @@ func (po *ProjectOptions) PromptForInputs() error {
// Process Templates
templateList := slicer.Interface()
options := slicer.String()
templateDetails, err := po.templates.GetTemplateDetails()
if err != nil {
return err
for _, templateDetails := range po.templates.TemplateList.details {
templateList.Add(templateDetails)
options.Add(fmt.Sprintf("%s - %s", templateDetails.Metadata.Name, templateDetails.Metadata.ShortDescription))
}
if po.Template != "" {
// Check template is valid if given
if templateDetails[po.Template] == nil {
keys := make([]string, 0, len(templateDetails))
for k := range templateDetails {
keys = append(keys, k)
}
return fmt.Errorf("invalid template name '%s'. Valid options: %s", po.Template, strings.Join(keys, ", "))
}
po.selectedTemplate = templateDetails[po.Template]
} else {
templateIndex := 0
keys := make([]string, 0)
for k := range templateDetails {
keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
templateDetail := templateDetails[k]
templateList.Add(templateDetail)
if !templateDetail.Metadata.PlatformSupported() {
templateDetail.Metadata.Name = "* " + templateDetail.Metadata.Name
}
options.Add(fmt.Sprintf("%s - %s", templateDetail.Metadata.Name, templateDetail.Metadata.ShortDescription))
}
templateIndex := 0
if len(options.AsSlice()) > 1 {
templateIndex = PromptSelection("Please select a template (* means unsupported on current platform)", options.AsSlice(), 0)
}
if len(templateList.AsSlice()) == 0 {
return fmt.Errorf("aborting: no templates found")
}
// After selection do this....
po.selectedTemplate = templateList.AsSlice()[templateIndex].(*TemplateDetails)
if len(options.AsSlice()) > 1 {
templateIndex = PromptSelection("Please select a template", options.AsSlice(), 0)
}
po.selectedTemplate.Metadata.Name = strings.TrimPrefix(po.selectedTemplate.Metadata.Name, "* ")
if !po.selectedTemplate.Metadata.PlatformSupported() {
println("WARNING: This template is unsupported on this platform!")
}
fmt.Println("Template: " + po.selectedTemplate.Metadata.Name)
// After selection do this....
po.selectedTemplate = templateList.AsSlice()[templateIndex].(*TemplateDetails)
// Setup NPM Project name
po.NPMProjectName = strings.ToLower(strings.Replace(po.Name, " ", "_", -1))
@@ -309,14 +218,14 @@ func (po *ProjectOptions) WriteProjectConfig() error {
return err
}
return os.WriteFile(targetFile, filedata, 0600)
return ioutil.WriteFile(targetFile, filedata, 0600)
}
// LoadConfig loads the project configuration file from the
// given directory
func (po *ProjectOptions) LoadConfig(projectDir string) error {
targetFile := filepath.Join(projectDir, "project.json")
rawBytes, err := os.ReadFile(targetFile)
rawBytes, err := ioutil.ReadFile(targetFile)
if err != nil {
return err
}
@@ -357,12 +266,18 @@ func processProjectName(po *ProjectOptions) {
po.Name = Prompt("The name of the project", "My Project")
}
fmt.Println("Project Name: " + po.Name)
}
func processBinaryName(po *ProjectOptions) {
if po.BinaryName == "" {
var binaryNameComputed = computeBinaryName(po.Name)
po.BinaryName = Prompt("The output binary name", binaryNameComputed)
if runtime.GOOS == "windows" {
if !strings.HasSuffix(po.BinaryName, ".exe") {
po.BinaryName += ".exe"
}
}
}
fmt.Println("Output binary Name: " + po.BinaryName)
}
@@ -398,9 +313,5 @@ func processTemplateMetadata(templateMetadata *TemplateMetadata, po *ProjectOpti
}
po.FrontEnd.Serve = templateMetadata.Serve
}
// Save platforms
po.Platforms = templateMetadata.Platforms
return nil
}

View File

@@ -4,6 +4,7 @@ import (
"bufio"
"fmt"
"os"
"runtime"
"strconv"
"strings"
)
@@ -19,7 +20,11 @@ func Prompt(question string, defaultValue ...string) string {
fmt.Printf(question + ": ")
reader := bufio.NewReader(os.Stdin)
input, _ := reader.ReadString('\n')
input = strings.TrimSpace(input)
EOL := "\n"
if runtime.GOOS == "windows" {
EOL = "\r\n"
}
input = strings.Replace(input, EOL, "", -1)
if input != "" {
answer = input

View File

@@ -3,7 +3,7 @@ package cmd
import (
"fmt"
"github.com/Masterminds/semver"
"github.com/masterminds/semver"
)
// SemanticVersion is a struct containing a semantic version
@@ -24,19 +24,11 @@ func NewSemanticVersion(version string) (*SemanticVersion, error) {
// IsRelease returns true if it's a release version
func (s *SemanticVersion) IsRelease() bool {
// Limit to v1
if s.Version.Major() != 1 {
return false
}
return len(s.Version.Prerelease()) == 0 && len(s.Version.Metadata()) == 0
}
// IsPreRelease returns true if it's a prerelease version
func (s *SemanticVersion) IsPreRelease() bool {
// Limit to v1
if s.Version.Major() != 1 {
return false
}
return len(s.Version.Prerelease()) > 0
}

View File

@@ -1,65 +0,0 @@
package cmd
import (
"testing"
)
func TestSemanticVersion_IsPreRelease(t *testing.T) {
tests := []struct {
name string
version string
want bool
}{
{"v1.6.7-pre0", "v1.6.7-pre0", true},
{"v2.6.7+pre0", "v2.6.7+pre0", false},
{"v2.6.7", "v2.6.7", false},
{"v2.0.0+alpha.1", "v2.0.0+alpha.1", false},
{"v2.0.0-alpha.1", "v2.0.0-alpha.1", false},
{"v1.6.7", "v1.6.7", false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
semanticversion, err := NewSemanticVersion(tt.version)
if err != nil {
t.Errorf("Invalid semantic version: %s", semanticversion)
return
}
s := &SemanticVersion{
Version: semanticversion.Version,
}
if got := s.IsPreRelease(); got != tt.want {
t.Errorf("IsPreRelease() = %v, want %v", got, tt.want)
}
})
}
}
func TestSemanticVersion_IsRelease(t *testing.T) {
tests := []struct {
name string
version string
want bool
}{
{"v1.6.7", "v1.6.7", true},
{"v2.6.7-pre0", "v2.6.7-pre0", false},
{"v2.6.7", "v2.6.7", false},
{"v2.6.7+release", "v2.6.7+release", false},
{"v2.0.0-alpha.1", "v2.0.0-alpha.1", false},
{"v1.6.7-pre0", "v1.6.7-pre0", false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
semanticversion, err := NewSemanticVersion(tt.version)
if err != nil {
t.Errorf("Invalid semantic version: %s", semanticversion)
return
}
s := &SemanticVersion{
Version: semanticversion.Version,
}
if got := s.IsRelease(); got != tt.want {
t.Errorf("IsRelease() = %v, want %v", got, tt.want)
}
})
}
}

View File

@@ -8,7 +8,6 @@ import (
// ShellHelper helps with Shell commands
type ShellHelper struct {
verbose bool
}
// NewShellHelper creates a new ShellHelper!
@@ -16,27 +15,16 @@ func NewShellHelper() *ShellHelper {
return &ShellHelper{}
}
// SetVerbose sets the verbose flag
func (sh *ShellHelper) SetVerbose() {
sh.verbose = true
}
// Run the given command
func (sh *ShellHelper) Run(command string, vars ...string) (stdout, stderr string, err error) {
cmd := exec.Command(command, vars...)
cmd.Env = append(os.Environ(), "GO111MODULE=on")
if !sh.verbose {
var stdo, stde bytes.Buffer
cmd.Stdout = &stdo
cmd.Stderr = &stde
err = cmd.Run()
stdout = string(stdo.Bytes())
stderr = string(stde.Bytes())
} else {
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Run()
}
var stdo, stde bytes.Buffer
cmd.Stdout = &stdo
cmd.Stderr = &stde
err = cmd.Run()
stdout = string(stdo.Bytes())
stderr = string(stde.Bytes())
return
}
@@ -45,17 +33,11 @@ func (sh *ShellHelper) RunInDirectory(dir string, command string, vars ...string
cmd := exec.Command(command, vars...)
cmd.Dir = dir
cmd.Env = append(os.Environ(), "GO111MODULE=on")
if !sh.verbose {
var stdo, stde bytes.Buffer
cmd.Stdout = &stdo
cmd.Stderr = &stde
err = cmd.Run()
stdout = string(stdo.Bytes())
stderr = string(stde.Bytes())
} else {
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Run()
}
var stdo, stde bytes.Buffer
cmd.Stdout = &stdo
cmd.Stderr = &stde
err = cmd.Run()
stdout = string(stdo.Bytes())
stderr = string(stde.Bytes())
return
}

View File

@@ -3,12 +3,14 @@ package cmd
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"os"
"path/filepath"
"runtime"
"strconv"
"time"
homedir "github.com/mitchellh/go-homedir"
)
// SystemHelper - Defines everything related to the system
@@ -36,11 +38,10 @@ func NewSystemHelper() *SystemHelper {
// setSystemDirs calculates the system directories it is interested in
func (s *SystemHelper) setSystemDirs() {
var err error
s.homeDir, err = os.UserHomeDir()
s.homeDir, err = homedir.Dir()
if err != nil {
log.Fatal("Cannot find home directory! Please file a bug report!")
}
// TODO: A better config system
s.wailsSystemDir = filepath.Join(s.homeDir, ".wails")
s.wailsSystemConfig = filepath.Join(s.wailsSystemDir, s.configFilename)
@@ -67,17 +68,6 @@ func (s *SystemHelper) ConfigFileIsValid() bool {
return err == nil
}
// GetAuthor returns a formatted string of the user's name and email
func (s *SystemHelper) GetAuthor() (string, error) {
var config *SystemConfig
config, err := s.LoadConfig()
if err != nil {
return "", err
}
return fmt.Sprintf("%s <%s>", config.Name, config.Email), nil
}
// BackupConfig attempts to backup the system config file
func (s *SystemHelper) BackupConfig() (string, error) {
now := strconv.FormatInt(time.Now().UTC().UnixNano(), 10)
@@ -98,16 +88,11 @@ func (s *SystemHelper) setup() error {
if config.Name != "" {
systemConfig["name"] = PromptRequired("What is your name", config.Name)
} else if n, err := getGitConfigValue("user.name"); err == nil && n != "" {
systemConfig["name"] = PromptRequired("What is your name", n)
} else {
systemConfig["name"] = PromptRequired("What is your name")
}
if config.Email != "" {
systemConfig["email"] = PromptRequired("What is your email address", config.Email)
} else if e, err := getGitConfigValue("user.email"); err == nil && e != "" {
systemConfig["email"] = PromptRequired("What is your email address", e)
} else {
systemConfig["email"] = PromptRequired("What is your email address")
}
@@ -123,7 +108,7 @@ func (s *SystemHelper) setup() error {
if err != nil {
return err
}
err = os.WriteFile(s.wailsSystemConfig, configData, 0755)
err = ioutil.WriteFile(s.wailsSystemConfig, configData, 0755)
if err != nil {
return err
}
@@ -136,7 +121,7 @@ func (s *SystemHelper) setup() error {
}
const introText = `
Wails is a lightweight framework for creating web-like desktop apps in Go.
Wails is a lightweight framework for creating web-like desktop apps in Go.
I'll need to ask you a few questions so I can fill in your project templates and then I will try and see if you have the correct dependencies installed. If you don't have the right tools installed, I'll try and suggest how to install them.
`
@@ -184,7 +169,7 @@ func (s *SystemHelper) Initialise() error {
return s.setup()
}
// SystemConfig - Defines system wide configuration data
// SystemConfig - Defines system wode configuration data
type SystemConfig struct {
Name string `json:"name"`
Email string `json:"email"`
@@ -206,11 +191,11 @@ func (sc *SystemConfig) Save(filename string) error {
}
// Write it out to the config file
return os.WriteFile(filename, theJSON, 0644)
return ioutil.WriteFile(filename, theJSON, 0644)
}
func (sc *SystemConfig) load(filename string) error {
configData, err := os.ReadFile(filename)
configData, err := ioutil.ReadFile(filename)
if err != nil {
return err
}
@@ -273,41 +258,45 @@ func CheckDependencies(logger *Logger) (bool, error) {
if err != nil {
return false, err
}
var libraryChecker CheckPkgInstalled
distroInfo := GetLinuxDistroInfo()
switch distroInfo.Distribution {
case Ubuntu, Debian, Zorin, Parrot, Linuxmint, Elementary, Kali, Neon, Deepin, Raspbian, PopOS, Uos:
libraryChecker = DpkgInstalled
case Arch, ArcoLinux, ArchLabs, Ctlos, Manjaro, ManjaroARM, EndeavourOS, ArtixLinux:
libraryChecker = PacmanInstalled
case CentOS, Fedora, Tumbleweed, Leap, RHEL:
libraryChecker = RpmInstalled
case Gentoo:
libraryChecker = EqueryInstalled
case VoidLinux:
libraryChecker = XbpsInstalled
case Solus:
libraryChecker = EOpkgInstalled
case Crux:
libraryChecker = PrtGetInstalled
case NixOS:
libraryChecker = NixEnvInstalled
default:
return false, RequestSupportForDistribution(distroInfo)
}
for _, library := range *requiredLibraries {
installed, err := libraryChecker(library.Name)
if err != nil {
return false, err
}
if !installed {
errors = true
logger.Error("Library '%s' not found. %s", library.Name, library.Help)
} else {
logger.Green("Library '%s' installed.", library.Name)
switch distroInfo.Distribution {
case Ubuntu:
installed, err := DpkgInstalled(library.Name)
if err != nil {
return false, err
}
if !installed {
errors = true
logger.Error("Library '%s' not found. %s", library.Name, library.Help)
} else {
logger.Green("Library '%s' installed.", library.Name)
}
case Arch:
installed, err := PacmanInstalled(library.Name)
if err != nil {
return false, err
}
if !installed {
errors = true
logger.Error("Library '%s' not found. %s", library.Name, library.Help)
} else {
logger.Green("Library '%s' installed.", library.Name)
}
case RedHat:
installed, err := RpmInstalled(library.Name)
if err != nil {
return false, err
}
if !installed {
errors = true
logger.Error("Library '%s' not found. %s", library.Name, library.Help)
} else {
logger.Green("Library '%s' installed.", library.Name)
}
default:
return false, fmt.Errorf("unable to check libraries on distribution '%s'. Please ensure that the '%s' equivalent is installed", distroInfo.DistributorID, library.Name)
}
}
}

View File

@@ -3,227 +3,115 @@ package cmd
import (
"bytes"
"encoding/json"
"fmt"
"log"
"os"
"path/filepath"
"runtime"
"regexp"
"strings"
"text/template"
"github.com/kennygrant/sanitize"
mewn "github.com/leaanthony/mewn"
mewnlib "github.com/leaanthony/mewn/lib"
"github.com/leaanthony/slicer"
)
// TemplateMetadata holds all the metadata for a Wails template
type TemplateMetadata struct {
Name string `json:"name"`
Version string `json:"version"`
ShortDescription string `json:"shortdescription"`
Description string `json:"description"`
Install string `json:"install"`
Build string `json:"build"`
Author string `json:"author"`
Created string `json:"created"`
FrontendDir string `json:"frontenddir"`
Serve string `json:"serve"`
Bridge string `json:"bridge"`
WailsDir string `json:"wailsdir"`
TemplateDependencies []*TemplateDependency `json:"dependencies,omitempty"`
// List of platforms that this template is supported on.
// No value means all platforms. A platform name is the same string
// as `runtime.GOOS` will return, eg: "darwin". NOTE: This is
// case sensitive.
Platforms []string `json:"platforms,omitempty"`
}
// PlatformSupported returns true if this template supports the
// currently running platform
func (m *TemplateMetadata) PlatformSupported() bool {
// Default is all platforms supported
if len(m.Platforms) == 0 {
return true
}
// Check that the platform is in the list
platformsSupported := slicer.String(m.Platforms)
return platformsSupported.Contains(runtime.GOOS)
}
// TemplateDependency defines a binary dependency for the template
// EG: ng for angular
type TemplateDependency struct {
Bin string `json:"bin"`
Help string `json:"help"`
Name string `json:"name"`
ShortDescription string `json:"shortdescription"`
Description string `json:"description"`
Install string `json:"install"`
Build string `json:"build"`
Author string `json:"author"`
Created string `json:"created"`
FrontendDir string `json:"frontenddir"`
Serve string `json:"serve"`
Bridge string `json:"bridge"`
}
// TemplateDetails holds information about a specific template
type TemplateDetails struct {
Name string
BasePath string
Path string
Metadata *TemplateMetadata
fs *FSHelper
}
// TemplateList is a list of available templates
type TemplateList struct {
details map[string]*TemplateDetails
}
// NewTemplateList creates a new TemplateList object
func NewTemplateList(filenames *mewnlib.FileGroup) *TemplateList {
// Iterate each template and store information
result := &TemplateList{details: make(map[string]*TemplateDetails)}
entries := slicer.String()
entries.AddSlice(filenames.Entries())
// Find all template.json files
metadataFiles := entries.Filter(func(filename string) bool {
match, _ := regexp.MatchString("(.)+template.json$", filename)
return match
})
// Load each metadata file
metadataFiles.Each(func(filename string) {
fileData := filenames.Bytes(filename)
var metadata TemplateMetadata
err := json.Unmarshal(fileData, &metadata)
if err != nil {
log.Fatalf("corrupt metadata for template: %s", filename)
}
path := strings.Split(filename, "/")[0]
thisTemplate := &TemplateDetails{Path: path, Metadata: &metadata}
result.details[filename] = thisTemplate
})
return result
}
// Template holds details about a Wails template
type Template struct {
Name string
Path string
Description string
}
// TemplateHelper is a utility object to help with processing templates
type TemplateHelper struct {
templateDir *Dir
fs *FSHelper
metadataFilename string
TemplateList *TemplateList
Files *mewnlib.FileGroup
}
// NewTemplateHelper creates a new template helper
func NewTemplateHelper() *TemplateHelper {
templateDir, err := fs.LocalDir("./templates")
if err != nil {
log.Fatal("Unable to find the template directory. Please reinstall Wails.")
}
files := mewn.Group("./templates")
return &TemplateHelper{
templateDir: templateDir,
metadataFilename: "template.json",
TemplateList: NewTemplateList(files),
Files: files,
}
}
// IsValidTemplate returns true if the given template name resides on disk
func (t *TemplateHelper) IsValidTemplate(templateName string) bool {
pathToTemplate := filepath.Join(t.templateDir.fullPath, templateName)
return t.fs.DirExists(pathToTemplate)
}
// SanitizeFilename sanitizes the given string to make a valid filename
func (t *TemplateHelper) SanitizeFilename(name string) string {
return sanitize.Name(name)
}
// CreateNewTemplate creates a new template based on the given directory name and string
func (t *TemplateHelper) CreateNewTemplate(dirname string, details *TemplateMetadata) (string, error) {
// Check if this template has already been created
if t.IsValidTemplate(dirname) {
return "", fmt.Errorf("cannot create template in directory '%s' - already exists", dirname)
}
targetDir := filepath.Join(t.templateDir.fullPath, dirname)
err := t.fs.MkDir(targetDir)
if err != nil {
return "", err
}
targetMetadata := filepath.Join(targetDir, t.metadataFilename)
err = t.fs.SaveAsJSON(details, targetMetadata)
return targetDir, err
}
// LoadMetadata loads the template's 'metadata.json' file
func (t *TemplateHelper) LoadMetadata(dir string) (*TemplateMetadata, error) {
templateFile := filepath.Join(dir, t.metadataFilename)
result := &TemplateMetadata{}
if !t.fs.FileExists(templateFile) {
return nil, nil
}
rawJSON, err := os.ReadFile(templateFile)
if err != nil {
return nil, err
}
err = json.Unmarshal(rawJSON, &result)
return result, err
}
// GetTemplateDetails returns a map of Template structs containing details
// of the found templates
func (t *TemplateHelper) GetTemplateDetails() (map[string]*TemplateDetails, error) {
// Get the subdirectory details
templateDirs, err := t.templateDir.GetSubdirs()
if err != nil {
return nil, err
}
result := make(map[string]*TemplateDetails)
for name, dir := range templateDirs {
result[name] = &TemplateDetails{
Path: dir,
}
metadata, err := t.LoadMetadata(dir)
if err != nil {
return nil, err
}
result[name].Metadata = metadata
if metadata.Name != "" {
result[name].Name = metadata.Name
} else {
// Ignore bad templates?
result[name] = nil
}
}
return result, nil
}
// GetTemplateFilenames returns all the filenames of the given template
func (t *TemplateHelper) GetTemplateFilenames(template *TemplateDetails) (*slicer.StringSlicer, error) {
// Get the subdirectory details
templateDir, err := t.fs.Directory(template.Path)
if err != nil {
return nil, err
}
return templateDir.GetAllFilenames()
}
// InstallTemplate installs the template given in the project options to the
// project path given
func (t *TemplateHelper) InstallTemplate(projectPath string, projectOptions *ProjectOptions) error {
// Check dependencies before installing
dependencies := projectOptions.selectedTemplate.Metadata.TemplateDependencies
if dependencies != nil {
programHelper := NewProgramHelper()
logger := NewLogger()
errors := []string{}
for _, dep := range dependencies {
program := programHelper.FindProgram(dep.Bin)
if program == nil {
errors = append(errors, dep.Help)
}
}
if len(errors) > 0 {
mainError := "template dependencies not installed"
if len(errors) == 1 {
mainError = errors[0]
} else {
for _, error := range errors {
logger.Red(error)
}
}
return fmt.Errorf(mainError)
}
}
// Get template files
templateFilenames, err := t.GetTemplateFilenames(projectOptions.selectedTemplate)
if err != nil {
return err
}
templatePath := projectOptions.selectedTemplate.Path
// Save the version
projectOptions.WailsVersion = Version
templateFilenames := slicer.String()
templateFilenames.AddSlice(projectOptions.templates.Files.Entries())
templateJSONFilename := filepath.Join(templatePath, t.metadataFilename)
templateJSONFilename := filepath.Join(templatePath, "template.json")
templateFiles := templateFilenames.Filter(func(filename string) bool {
filename = filepath.FromSlash(filename)
return strings.HasPrefix(filename, templatePath) && filename != templateJSONFilename
})
var err error
templateFiles.Each(func(templateFile string) {
// Setup filenames
@@ -232,14 +120,11 @@ func (t *TemplateHelper) InstallTemplate(projectPath string, projectOptions *Pro
if err != nil {
return
}
filedata, err := t.fs.LoadAsBytes(templateFile)
if err != nil {
return
}
filedata := projectOptions.templates.Files.Bytes(templateFile)
// If file is a template, process it
if strings.HasSuffix(templateFile, ".template") {
templateData := string(filedata)
templateData := projectOptions.templates.Files.String(templateFile)
tmpl := template.New(templateFile)
tmpl.Parse(templateData)
var tpl bytes.Buffer

View File

@@ -1,13 +0,0 @@
# Editor configuration, see https://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
max_line_length = off
trim_trailing_whitespace = false

View File

@@ -1,47 +0,0 @@
# See http://help.github.com/ignore-files/ for more about ignoring files.
# compiled output
/dist
/tmp
/out-tsc
# Only exists if Bazel was run
/bazel-out
# dependencies
/node_modules
# profiling files
chrome-profiler-events.json
speed-measure-plugin.json
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.history/*
# misc
/.sass-cache
/connect.lock
/coverage
/libpeerconnection.log
npm-debug.log
yarn-error.log
testem.log
/typings
# System Files
.DS_Store
Thumbs.db
.editorcinfig

View File

@@ -1,27 +0,0 @@
# MyApp
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 8.0.3.
## Development server
Run `npx ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
## Code scaffolding
Run `npx ng generate component component-name` to generate a new component. You can also use `npx ng generate directive|pipe|service|class|guard|interface|enum|module`.
## Build
Run `npx ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
## Running unit tests
Run `npx ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
## Running end-to-end tests
Run `npx ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
## Further help
To get more help on the Angular CLI use `npx ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).

View File

@@ -1,121 +0,0 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"my-app": {
"projectType": "application",
"schematics": {},
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": {
"build": {
"builder": "ngx-build-plus:browser",
"options": {
"outputPath": "dist/my-app",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.app.json",
"aot": false,
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"src/styles.css"
],
"scripts": []
},
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"budgets": [
{
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "5mb"
}
]
}
}
},
"serve": {
"builder": "ngx-build-plus:dev-server",
"options": {
"browserTarget": "my-app:build"
},
"configurations": {
"production": {
"browserTarget": "my-app:build:production"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "my-app:build"
}
},
"test": {
"builder": "ngx-build-plus:karma",
"options": {
"main": "src/test.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.spec.json",
"karmaConfig": "karma.conf.js",
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"src/styles.css"
],
"scripts": []
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"tsconfig.app.json",
"tsconfig.spec.json",
"e2e/tsconfig.json"
],
"exclude": [
"**/node_modules/**"
]
}
},
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "e2e/protractor.conf.js",
"devServerTarget": "my-app:serve"
},
"configurations": {
"production": {
"devServerTarget": "my-app:serve:production"
}
}
}
}
}
},
"defaultProject": "my-app"
}

View File

@@ -1,12 +0,0 @@
# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
# For additional information regarding the format and rule options, please see:
# https://github.com/browserslist/browserslist#queries
# You can see what browsers were selected by your queries by running:
# npx browserslist
> 0.5%
last 2 versions
Firefox ESR
not dead
IE 9-11 # For IE 9-11 support, remove 'not'.

View File

@@ -1,32 +0,0 @@
// @ts-check
// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts
const { SpecReporter } = require('jasmine-spec-reporter');
/**
* @type { import("protractor").Config }
*/
exports.config = {
allScriptsTimeout: 11000,
specs: [
'./src/**/*.e2e-spec.ts'
],
capabilities: {
'browserName': 'chrome'
},
directConnect: true,
baseUrl: 'http://localhost:4200/',
framework: 'jasmine',
jasmineNodeOpts: {
showColors: true,
defaultTimeoutInterval: 30000,
print: function() {}
},
onPrepare() {
require('ts-node').register({
project: require('path').join(__dirname, './tsconfig.json')
});
jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
}
};

View File

@@ -1,23 +0,0 @@
import { AppPage } from './app.po';
import { browser, logging } from 'protractor';
describe('workspace-project App', () => {
let page: AppPage;
beforeEach(() => {
page = new AppPage();
});
it('should display welcome message', () => {
page.navigateTo();
expect(page.getTitleText()).toEqual('Welcome to my-app!');
});
afterEach(async () => {
// Assert that there are no errors emitted from the browser
const logs = await browser.manage().logs().get(logging.Type.BROWSER);
expect(logs).not.toContain(jasmine.objectContaining({
level: logging.Level.SEVERE,
} as logging.Entry));
});
});

View File

@@ -1,11 +0,0 @@
import { browser, by, element } from 'protractor';
export class AppPage {
navigateTo() {
return browser.get(browser.baseUrl) as Promise<any>;
}
getTitleText() {
return element(by.css('app-root h1')).getText() as Promise<string>;
}
}

View File

@@ -1,13 +0,0 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/e2e",
"module": "commonjs",
"target": "es5",
"types": [
"jasmine",
"jasminewd2",
"node"
]
}
}

View File

@@ -1,32 +0,0 @@
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('@angular-devkit/build-angular/plugins/karma')
],
client: {
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, './coverage/my-app'),
reports: ['html', 'lcovonly', 'text-summary'],
fixWebpackSourcePaths: true
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false,
restartOnFileChange: true
});
};

View File

@@ -1,52 +0,0 @@
{
"name": "my-app",
"version": "0.0.0",
"scripts": {
"ng": "npx ng",
"serve": "npx ng serve --poll=2000 --host=0.0.0.0",
"build": "npx ng build --single-bundle true --output-hashing none --prod --bundle-styles false",
"test": "npx ng test",
"lint": "npx ng lint",
"e2e": "npx ng e2e"
},
"private": true,
"dependencies": {
"@angular/animations": "^8.0.2",
"@angular/cdk": "^8.0.1",
"@angular/common": "~8.0.1",
"@angular/compiler": "~8.0.1",
"@angular/core": "~8.0.1",
"@angular/forms": "~8.0.1",
"@angular/material": "^8.0.1",
"@angular/platform-browser": "~8.0.1",
"@angular/platform-browser-dynamic": "~8.0.1",
"@angular/router": "~8.0.1",
"@wailsapp/runtime": "^1.0.0",
"core-js": "^3.4.4",
"ngx-build-plus": "^8.0.3",
"rxjs": "~6.4.0",
"tslib": "^1.9.0",
"zone.js": "~0.9.1"
},
"devDependencies": {
"@angular-devkit/build-angular": "~0.800.0",
"@angular/cli": "~8.0.3",
"@angular/compiler-cli": "~8.0.1",
"@angular/language-service": "~8.0.1",
"@types/node": "~8.9.4",
"@types/jasmine": "~3.3.8",
"@types/jasminewd2": "~2.0.3",
"codelyzer": "^5.0.0",
"jasmine-core": "~3.4.0",
"jasmine-spec-reporter": "~4.2.1",
"karma": "~4.1.0",
"karma-chrome-launcher": "~2.2.0",
"karma-coverage-istanbul-reporter": "~2.0.1",
"karma-jasmine": "~2.0.1",
"karma-jasmine-html-reporter": "^1.4.0",
"protractor": "~5.4.0",
"ts-node": "~7.0.0",
"tslint": "~5.15.0",
"typescript": "~3.4.3"
}
}

View File

@@ -1,13 +0,0 @@
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
const routes: Routes = [];
@NgModule({
imports: [
RouterModule.forRoot(routes,{useHash:true})
],
exports: [RouterModule]
})
export class AppRoutingModule { }

View File

@@ -1,14 +0,0 @@
<!--The content below is only a placeholder and can be replaced.-->
<div style="text-align:center">
<h1>
Welcome to {{ title }}!
</h1>
<img width="300" alt="Angular Logo"
src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTAgMjUwIj4KICAgIDxwYXRoIGZpbGw9IiNERDAwMzEiIGQ9Ik0xMjUgMzBMMzEuOSA2My4ybDE0LjIgMTIzLjFMMTI1IDIzMGw3OC45LTQzLjcgMTQuMi0xMjMuMXoiIC8+CiAgICA8cGF0aCBmaWxsPSIjQzMwMDJGIiBkPSJNMTI1IDMwdjIyLjItLjFWMjMwbDc4LjktNDMuNyAxNC4yLTEyMy4xTDEyNSAzMHoiIC8+CiAgICA8cGF0aCAgZmlsbD0iI0ZGRkZGRiIgZD0iTTEyNSA1Mi4xTDY2LjggMTgyLjZoMjEuN2wxMS43LTI5LjJoNDkuNGwxMS43IDI5LjJIMTgzTDEyNSA1Mi4xem0xNyA4My4zaC0zNGwxNy00MC45IDE3IDQwLjl6IiAvPgogIDwvc3ZnPg==">
<br />
<button (click)="onClickMe()">Hello</button>
<p>{{clickMessage}}</p>
</div>
<router-outlet></router-outlet>

View File

@@ -1,35 +0,0 @@
import { TestBed, async } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { AppComponent } from './app.component';
describe('AppComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
RouterTestingModule
],
declarations: [
AppComponent
],
}).compileComponents();
}));
it('should create the app', () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app).toBeTruthy();
});
it(`should have as title 'my-app'`, () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app.title).toEqual('my-app');
});
it('should render title in a h1 tag', () => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled = fixture.debugElement.nativeElement;
expect(compiled.querySelector('h1').textContent).toContain('Welcome to my-app!');
});
});

View File

@@ -1,19 +0,0 @@
import { Component } from '@angular/core';
@Component({
selector: '[id="app"]',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'my-app';
clickMessage = '';
onClickMe() {
// @ts-ignore
window.backend.basic().then(result =>
this.clickMessage = result
);
}
}

View File

@@ -1,20 +0,0 @@
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { APP_BASE_HREF } from '@angular/common';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
AppRoutingModule
],
providers: [{provide: APP_BASE_HREF, useValue : '/' }],
bootstrap: [AppComponent]
})
export class AppModule { }

View File

@@ -1,3 +0,0 @@
export const environment = {
production: true
};

View File

@@ -1,16 +0,0 @@
// This file can be replaced during build by using the `fileReplacements` array.
// `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
// The list of file replacements can be found in `angular.json`.
export const environment = {
production: false
};
/*
* For easier debugging in development mode, you can import the following file
* to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
*
* This import should be commented out in production mode because it will have a negative impact
* on performance if an error is thrown.
*/
// import 'zone.js/dist/zone-error'; // Included with Angular CLI.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

View File

@@ -1,14 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>my-app</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<div id="app"></div>
</body>
</html>

View File

@@ -1,19 +0,0 @@
import 'core-js/stable';
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
import 'zone.js'
import * as Wails from '@wailsapp/runtime';
if (environment.production) {
enableProdMode();
}
Wails.Init(() => {
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.error(err));
});

View File

@@ -1,63 +0,0 @@
/**
* This file includes polyfills needed by Angular and is loaded before the app.
* You can add your own extra polyfills to this file.
*
* This file is divided into 2 sections:
* 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
* 2. Application imports. Files imported after ZoneJS that should be loaded before your main
* file.
*
* The current setup is for so-called "evergreen" browsers; the last versions of browsers that
* automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
* Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
*
* Learn more in https://angular.io/guide/browser-support
*/
/***************************************************************************************************
* BROWSER POLYFILLS
*/
/** IE10 and IE11 requires the following for NgClass support on SVG elements */
// import 'classlist.js'; // Run `npm install --save classlist.js`.
/**
* Web Animations `@angular/platform-browser/animations`
* Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
* Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
*/
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
/**
* By default, zone.js will patch all possible macroTask and DomEvents
* user can disable parts of macroTask/DomEvents patch by setting following flags
* because those flags need to be set before `zone.js` being loaded, and webpack
* will put import in the top of bundle, so user need to create a separate file
* in this directory (for example: zone-flags.ts), and put the following flags
* into that file, and then add the following code before importing zone.js.
* import './zone-flags.ts';
*
* The flags allowed in zone-flags.ts are listed here.
*
* The following flags will work for all browsers.
*
* (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
* (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
* (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
*
* in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
* with the following flag, it will bypass `zone.js` patch for IE/Edge
*
* (window as any).__Zone_enable_cross_context_check = true;
*
*/
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
*/
//import 'zone.js/dist/zone'; // Included with Angular CLI.
/***************************************************************************************************
* APPLICATION IMPORTS
*/

View File

@@ -1,24 +0,0 @@
/* You can add global styles to this file, and also import other style files */
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
"Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
background-color: #282c34;
}
p {
color: white
}
h1 {
color: white
}
button {
background-color: white;
color: black;
}

View File

@@ -1,20 +0,0 @@
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'zone.js/dist/zone-testing';
import { getTestBed } from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';
declare const require: any;
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting()
);
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules.
context.keys().map(context);

View File

@@ -1,14 +0,0 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/app",
"types": []
},
"include": [
"src/**/*.ts"
],
"exclude": [
"src/test.ts",
"src/**/*.spec.ts"
]
}

View File

@@ -1,23 +0,0 @@
{
"compileOnSave": false,
"compilerOptions": {
"baseUrl": "./",
"outDir": "./dist/out-tsc",
"sourceMap": true,
"declaration": false,
"downlevelIteration": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"module": "esnext",
"moduleResolution": "node",
"importHelpers": true,
"target": "es5",
"typeRoots": [
"node_modules/@types"
],
"lib": [
"es2018",
"dom"
]
}
}

View File

@@ -1,18 +0,0 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/spec",
"types": [
"jasmine",
"node"
]
},
"files": [
"src/test.ts",
"src/polyfills.ts"
],
"include": [
"src/**/*.spec.ts",
"src/**/*.d.ts"
]
}

View File

@@ -1,92 +0,0 @@
{
"extends": "tslint:recommended",
"rules": {
"array-type": false,
"arrow-parens": false,
"deprecation": {
"severity": "warn"
},
"component-class-suffix": true,
"contextual-lifecycle": true,
"directive-class-suffix": true,
"directive-selector": [
true,
"attribute",
"app",
"camelCase"
],
"component-selector": [
true,
"element",
"app",
"kebab-case"
],
"import-blacklist": [
true,
"rxjs/Rx"
],
"interface-name": false,
"max-classes-per-file": false,
"max-line-length": [
true,
140
],
"member-access": false,
"member-ordering": [
true,
{
"order": [
"static-field",
"instance-field",
"static-method",
"instance-method"
]
}
],
"no-consecutive-blank-lines": false,
"no-console": [
true,
"debug",
"info",
"time",
"timeEnd",
"trace"
],
"no-empty": false,
"no-inferrable-types": [
true,
"ignore-params"
],
"no-non-null-assertion": true,
"no-redundant-jsdoc": true,
"no-switch-case-fall-through": true,
"no-use-before-declare": true,
"no-var-requires": false,
"object-literal-key-quotes": [
true,
"as-needed"
],
"object-literal-sort-keys": false,
"ordered-imports": false,
"quotemark": [
true,
"single"
],
"trailing-comma": false,
"no-conflicting-lifecycle": true,
"no-host-metadata-property": true,
"no-input-rename": true,
"no-inputs-metadata-property": true,
"no-output-native": true,
"no-output-on-prefix": true,
"no-output-rename": true,
"no-outputs-metadata-property": true,
"template-banana-in-box": true,
"template-no-negated-async": true,
"use-lifecycle-interface": true,
"use-pipe-transform-interface": true
},
"rulesDirectory": [
"codelyzer"
]
}

View File

@@ -1,5 +0,0 @@
module {{.BinaryName}}
require (
github.com/wailsapp/wails {{.WailsVersion}}
)

View File

@@ -1,30 +0,0 @@
package main
import (
_ "embed"
"github.com/wailsapp/wails"
)
func basic() string {
return "World!"
}
//go:embed frontend/dist/my-app/main.js
var js string
//go:embed frontend/dist/my-app/styles.css
var css string
func main() {
app := wails.CreateApp(&wails.AppConfig{
Width: 1024,
Height: 768,
Title: "{{.Name}}",
JS: js,
CSS: css,
Colour: "#131313",
})
app.Bind(basic)
app.Run()
}

View File

@@ -1,20 +0,0 @@
{
"name": "Angular",
"version": "1.0.0",
"shortdescription": "Angular 8 template (Requires node 10.8+)",
"description": "Angular projects w/ @angular/cli - Note: in order to reach the cli use npx like this: npx ng",
"dependencies": [
{
"bin": "npx",
"help": "This template requires 'npx'. Please install with 'npm install -g npx'"
}
],
"install": "npm install",
"build": "npx ng build --single-bundle true --output-hashing none --prod --bundle-styles false",
"author": "bh90210 <ktc@pm.me>",
"created": "2019-06-15 18:23:48.666414555 +0300 EEST m=+223.934866008",
"frontenddir": "frontend",
"serve": "npm run serve",
"bridge": "src",
"wailsdir": ""
}

View File

@@ -1,3 +0,0 @@
{
"esversion": 6
}

View File

@@ -1,23 +0,0 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*

View File

@@ -1,68 +0,0 @@
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
## Available Scripts
In the project directory, you can run:
### `npm start`
Runs the app in the development mode.<br>
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
The page will reload if you make edits.<br>
You will also see any lint errors in the console.
### `npm test`
Launches the test runner in the interactive watch mode.<br>
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
### `npm run build`
Builds the app for production to the `build` folder.<br>
It correctly bundles React in production mode and optimizes the build for the best performance.
The build is minified and the filenames include the hashes.<br>
Your app is ready to be deployed!
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
### `npm run eject`
**Note: this is a one-way operation. Once you `eject`, you cant go back!**
If you arent satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
Instead, it will copy all the configuration files and the transitive dependencies (Webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point youre on your own.
You dont have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldnt feel obligated to use this feature. However we understand that this tool wouldnt be useful if you couldnt customize it when you are ready for it.
## Learn More
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
To learn React, check out the [React documentation](https://reactjs.org/).
### Code Splitting
This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting
### Analyzing the Bundle Size
This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size
### Making a Progressive Web App
This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app
### Advanced Configuration
This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration
### Deployment
This section has moved here: https://facebook.github.io/create-react-app/docs/deployment
### `npm run build` fails to minify
This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify

View File

@@ -1,35 +0,0 @@
{
"name": "{{.NPMProjectName}}",
"author": "{{.Author.Name}}<{{.Author.Email}}>",
"version": "0.1.0",
"private": true,
"dependencies": {
"core-js": "^3.6.4",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"wails-react-scripts": "3.0.1-2",
"react-modal": "3.11.2",
"@wailsapp/runtime": "^1.0.10"
},
"scripts": {
"serve": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

View File

@@ -1,43 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta name="description" content="Web site created using create-react-app" />
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="app"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 176 KiB

View File

@@ -1,25 +0,0 @@
{
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}

View File

@@ -1,3 +0,0 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:

View File

@@ -1,38 +0,0 @@
.App {
text-align: center;
}
.App-logo {
height: 40vmin;
pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-spin infinite 20s linear;
}
}
.App-header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}
.App-link {
color: #61dafb;
}
@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}

Some files were not shown because too many files have changed in this diff Show More