Manager: in setup assistant, find Blender in macOS default install dir

On macOS, automatically find Blender when it is installed in the default
installation directory (`/Applications/Blender.app`)
This commit is contained in:
Sybren A. Stüvel 2024-10-03 21:15:07 +02:00
parent cbf82e4a55
commit d61f6c9e14
6 changed files with 106 additions and 4 deletions

View File

@ -0,0 +1,45 @@
//go:build darwin
package find_blender
import (
"os"
"github.com/rs/zerolog/log"
)
// SPDX-License-Identifier: GPL-3.0-or-later
const (
blenderExeName = "Blender"
defaultPath = "/Applications/Blender.app/Contents/MacOS/Blender"
)
// fileAssociation isn't implemented on non-Windows platforms.
func fileAssociation() (string, error) {
return "", ErrNotAvailable
}
// searchDefaultPaths search any available platform-specific default locations for Blender to be in.
// Returns the path of the blender executable, or an empty string if nothing is found.
func searchDefaultPaths() string {
stat, err := os.Stat(defaultPath)
switch {
case os.IsNotExist(err):
return ""
case err != nil:
log.Warn().
AnErr("cause", err).
Str("path", defaultPath).
Msg("could not check default path, ignoring")
return ""
case stat.IsDir():
log.Warn().
Str("path", defaultPath).
Msg("expected Blender executable, but is a directory, ignoring")
return ""
}
return defaultPath
}

View File

@ -56,8 +56,7 @@ func CheckBlender(ctx context.Context, exename string) (CheckBlenderResult, erro
fullPath, err := fileAssociation() fullPath, err := fileAssociation()
switch { switch {
case errors.Is(err, ErrNotAvailable): case errors.Is(err, ErrNotAvailable):
// Association finder not available, act as if "blender" was given as exename. break
return CheckBlender(ctx, "blender")
case err != nil: case err != nil:
// Some other error occurred, better to report it. // Some other error occurred, better to report it.
return CheckBlenderResult{}, fmt.Errorf("finding .blend file association: %w", err) return CheckBlenderResult{}, fmt.Errorf("finding .blend file association: %w", err)
@ -65,6 +64,17 @@ func CheckBlender(ctx context.Context, exename string) (CheckBlenderResult, erro
// The full path was found, report the Blender version. // The full path was found, report the Blender version.
return getResultWithVersion(ctx, exename, fullPath, api.BlenderPathSourceFileAssociation) return getResultWithVersion(ctx, exename, fullPath, api.BlenderPathSourceFileAssociation)
} }
// Try some platform-specific default directories.
fullPath = searchDefaultPaths()
if fullPath != "" {
result, err := CheckBlender(ctx, fullPath)
result.Source = api.BlenderPathSourceSystemLocation
return result, err
}
// Association finder not available, act as if "blender" was given as exename.
return CheckBlender(ctx, "blender")
} }
if crosspath.Dir(exename) != "." { if crosspath.Dir(exename) != "." {
@ -77,6 +87,7 @@ func CheckBlender(ctx context.Context, exename string) (CheckBlenderResult, erro
if err != nil { if err != nil {
return CheckBlenderResult{}, err return CheckBlenderResult{}, err
} }
return getResultWithVersion(ctx, exename, fullPath, api.BlenderPathSourcePathEnvvar) return getResultWithVersion(ctx, exename, fullPath, api.BlenderPathSourcePathEnvvar)
} }

View File

@ -1,4 +1,4 @@
//go:build !windows //go:build !windows && !darwin
package find_blender package find_blender
@ -6,6 +6,12 @@ package find_blender
const blenderExeName = "blender" const blenderExeName = "blender"
// searchDefaultPaths search any available platform-specific default locations for Blender to be in.
// Returns the path of the blender executable, or an empty string if nothing is found.
func searchDefaultPaths() string {
return ""
}
// fileAssociation isn't implemented on non-Windows platforms. // fileAssociation isn't implemented on non-Windows platforms.
func fileAssociation() (string, error) { func fileAssociation() (string, error) {
return "", ErrNotAvailable return "", ErrNotAvailable

View File

@ -16,6 +16,12 @@ import (
const blenderExeName = "blender.exe" const blenderExeName = "blender.exe"
// searchDefaultPaths search any available platform-specific default locations for Blender to be in.
// Returns the path of the blender executable, or an empty string if nothing is found.
func searchDefaultPaths() string {
return ""
}
// fileAssociation returns the full path of `blender.exe` associated with ".blend" files. // fileAssociation returns the full path of `blender.exe` associated with ".blend" files.
func fileAssociation() (string, error) { func fileAssociation() (string, error) {
exe, err := getFileAssociation(".blend") exe, err := getFileAssociation(".blend")

View File

@ -361,7 +361,9 @@ func checkSetupAssistantConfig(config api.SetupAssistantConfig) error {
return ErrSetupConfigEmptyPath return ErrSetupConfigEmptyPath
} }
case api.BlenderPathSourceInputPath, api.BlenderPathSourcePathEnvvar: case api.BlenderPathSourceInputPath,
api.BlenderPathSourcePathEnvvar,
api.BlenderPathSourceSystemLocation:
if config.BlenderExecutable.Path == "" || if config.BlenderExecutable.Path == "" ||
config.BlenderExecutable.Input == "" { config.BlenderExecutable.Input == "" {
return ErrSetupConfigEmptyPathOrInput return ErrSetupConfigEmptyPathOrInput

View File

@ -163,6 +163,30 @@
</div> </div>
</label> </label>
<label v-if="autoFoundBlenderSystemLocation" for="blender-default_location">
<div>
<input
v-model="selectedBlender"
:value="autoFoundBlenderSystemLocation"
id="blender-default_location"
name="blender"
type="radio" />
{{ sourceLabels[autoFoundBlenderSystemLocation.source] }}
</div>
<div class="setup-path-command">
<span class="path">
{{ autoFoundBlenderSystemLocation.path }}
</span>
<span
aria-label="Console output when running with --version"
class="command-preview"
data-microtip-position="top"
role="tooltip">
{{ autoFoundBlenderSystemLocation.cause }}
</span>
</div>
</label>
<label for="blender-input_path"> <label for="blender-input_path">
<div> <div>
<input <input
@ -230,6 +254,9 @@
>" as found on <code>$PATH</code> (currently "<code>{{ selectedBlender.path }}</code >" as found on <code>$PATH</code> (currently "<code>{{ selectedBlender.path }}</code
>") >")
</dd> </dd>
<dd v-if="selectedBlender.source == 'system_location'">
Standard on your system: "<code>{{ selectedBlender.path }}</code>"
</dd>
<dd v-if="selectedBlender.source == 'input_path'"> <dd v-if="selectedBlender.source == 'input_path'">
The command you provided: "<code>{{ selectedBlender.path }}</code The command you provided: "<code>{{ selectedBlender.path }}</code
>" >"
@ -289,6 +316,7 @@ export default {
file_association: 'Blender that runs when you double-click a .blend file:', file_association: 'Blender that runs when you double-click a .blend file:',
path_envvar: 'Blender found on the $PATH environment:', path_envvar: 'Blender found on the $PATH environment:',
input_path: 'Specify a Blender executable:', input_path: 'Specify a Blender executable:',
system_location: 'Standard location of Blender on your system:',
default: 'Skip, let the Workers use whatever Blender is available.', default: 'Skip, let the Workers use whatever Blender is available.',
}, },
isConfirming: false, isConfirming: false,
@ -319,6 +347,9 @@ export default {
autoFoundBlenderFileAssociation() { autoFoundBlenderFileAssociation() {
return this.autoFoundBlenders.find((b) => b.source === 'file_association'); return this.autoFoundBlenders.find((b) => b.source === 'file_association');
}, },
autoFoundBlenderSystemLocation() {
return this.autoFoundBlenders.find((b) => b.source === 'system_location');
},
blenderFromInputPath() { blenderFromInputPath() {
return this.allBlenders.find((b) => b.source === 'input_path'); return this.allBlenders.find((b) => b.source === 'input_path');
}, },
@ -340,6 +371,7 @@ export default {
}, },
}, },
mounted() { mounted() {
window.assist = this;
this.findBlenderExePath(); this.findBlenderExePath();
document.body.classList.add('is-setup-assistant'); document.body.classList.add('is-setup-assistant');