Skip to content

Commit

Permalink
v0.3.0
Browse files Browse the repository at this point in the history
  * [compatibility] Added support for using an available `python3` on macOS 12.3+, where the system v2.x `/usr/bin/python` will no longer be avaialble.
  • Loading branch information
mklement0 committed Feb 11, 2022
1 parent cde23ae commit be6b1de
Show file tree
Hide file tree
Showing 9 changed files with 4,456 additions and 2,253 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Expand Up @@ -4,6 +4,9 @@ Versioning complies with [semantic versioning (semver)](http://semver.org/).

<!-- NOTE: An entry template for a new version is automatically added each time `make version` is called. Fill in changes afterwards. -->

* **[v0.3.0](https://github.com/mklement0/fileicon/compare/v0.2.4...v0.3.0)** (2022-02-11):
* [compatibility] Added support for using an available `python3` on macOS 12.3+, where the system v2.x `/usr/bin/python` will no longer be avaialble.

* **[v0.2.4](https://github.com/mklement0/fileicon/compare/v0.2.3...v0.2.4)** (2019-12-10):
* [installation] Thanks to @danielbayley, there is now an official Homebrew formula.

Expand Down
2 changes: 1 addition & 1 deletion LICENSE.md
@@ -1 +1 @@
Copyright (c) 2015-2019 Michael Klement <mklement0@gmail.com> (http://same2u.net), released under the [MIT license](https://spdx.org/licenses/MIT#licenseText).
Copyright (c) 2015-2022 Michael Klement <mklement0@gmail.com> (http://same2u.net), released under the [MIT license](https://spdx.org/licenses/MIT#licenseText).
2 changes: 1 addition & 1 deletion Makefile
Expand Up @@ -53,7 +53,7 @@ ifeq ($(NOTEST),1)
@echo Note: Skipping tests, as requested. >&2
else
@exists() { [ -e "$$1" ]; }; exists ./test/* || { echo "(No tests defined.)" >&2; exit 0; }; \
if [[ -n $$(json -f package.json main) ]]; then tap ./test; else urchin ./test; fi
if [[ -n $$(json -f package.json main) ]]; then tap ./test; else urchin ./test && [[ -x /usr/bin/python ]] && { printf '\n=== Re-running tests with Python3... ====\n'; __FILEICON_USEPY3=1 urchin ./test; } fi
endif

# Commits (with prompt for message) and pushes to the branch of the same name in remote repo 'origin',
Expand Down
23 changes: 17 additions & 6 deletions README.md
Expand Up @@ -5,7 +5,7 @@

**Contents**

- [fileicon &mdash; introduction](#fileicon-&mdash-introduction)
- [fileicon &mdash; introduction](#fileicon-mdash-introduction)
- [Examples](#examples)
- [Installation](#installation)
- [Installation via Homebrew](#installation-via-homebrew)
Expand Down Expand Up @@ -69,6 +69,14 @@ fileicon test foodir

* **macOS**

**Important**: To assign icons (subcommand `set`), Python is required. On macOS versions up to 12.2 (run `sw_vers` and look for the `ProductVersion` field),
`fileicon` uses the system (built-in) v2.x Python at `/usr/bin/python`, but the latter will be removed in 12.3.
If `/usr/bin/python` isn't present, `fileicon` will attempt to use `python3`, via the system's path.
By default, a _stub_ executable at `/usr/bin/python3` comes with macOS, which then triggers a prompt to
dowloand and install Python 3 as part of the Xcode command-line utilities. You'll have to perform this
installation on demand, or use a Homebrew-installed Python3 version (`brew install python@3`).
Once Python 3 is installed, the also required select `pyobjc-*` packages will be installed on demand at the user level, the first time `set` is called.

## Installation via Homebrew

With [Homebrew](https://brew.sh/) installed, run the following:
Expand Down Expand Up @@ -137,7 +145,7 @@ Standard options: --help, --man, --version, --home

# License

Copyright (c) 2015-2019 Michael Klement <mklement0@gmail.com> (http://same2u.net), released under the [MIT license](https://spdx.org/licenses/MIT#licenseText).
Copyright (c) 2015-2022 Michael Klement <mklement0@gmail.com> (http://same2u.net), released under the [MIT license](https://spdx.org/licenses/MIT#licenseText).

## Acknowledgements

Expand All @@ -149,13 +157,13 @@ This project gratefully depends on the following open-source components, accordi

## npm dependencies

* [doctoc (D)](https://github.com/thlorenz/doctoc#readme)
* [doctoc (D)]()
* [json (D)](https://github.com/trentm/json#readme)
* [marked (D)](https://marked.js.org)
* [marked (D)](https://github.com/chjj/marked)
* [marked-man (D)](https://github.com/kapouer/marked-man#readme)
* [replace (D)](https://github.com/ALMaclaine/replace#readme)
* [replace (D)]()
* [semver (D)](https://github.com/npm/node-semver#readme)
* [tap (D)](http://www.node-tap.org/)
* [tap (D)](http://node-tap.org/)
* [urchin (D)](https://github.com/tlevine/urchin#readme)

<!-- DO NOT EDIT THE NEXT CHAPTER and RETAIN THIS COMMENT: The next chapter is updated by `make update-readme/release` with the contents of 'CHANGELOG.md'. ALSO, LEAVE AT LEAST 1 BLANK LINE AFTER THIS COMMENT. -->
Expand All @@ -166,6 +174,9 @@ Versioning complies with [semantic versioning (semver)](http://semver.org/).

<!-- NOTE: An entry template for a new version is automatically added each time `make version` is called. Fill in changes afterwards. -->

* **[v0.3.0](https://github.com/mklement0/fileicon/compare/v0.2.4...v0.3.0)** (2022-02-11):
* [compatibility] Added support for using an available `python3` on macOS 12.3+, where the system v2.x `/usr/bin/python` will no longer be avaialble.

* **[v0.2.4](https://github.com/mklement0/fileicon/compare/v0.2.3...v0.2.4)** (2019-12-10):
* [installation] Thanks to @danielbayley, there is now an official Homebrew formula.

Expand Down
64 changes: 56 additions & 8 deletions bin/fileicon
Expand Up @@ -12,7 +12,7 @@

kTHIS_NAME=${BASH_SOURCE##*/}
kTHIS_HOMEPAGE='https://github.com/mklement0/fileicon'
kTHIS_VERSION='v0.2.4' # NOTE: This assignment is automatically updated by `make version VER=<newVer>` - DO keep the 'v' prefix.
kTHIS_VERSION='v0.3.0' # NOTE: This assignment is automatically updated by `make version VER=<newVer>` - DO keep the 'v' prefix.

unset CDPATH # To prevent unpredictable `cd` behavior.

Expand Down Expand Up @@ -85,7 +85,7 @@ printUsage() {
case $1 in
--version)
# Output version number and exit, if requested.
ver="v0.2.4"; echo "$kTHIS_NAME $kTHIS_VERSION"$'\nFor license information and more, visit '"$kTHIS_HOMEPAGE"; exit 0
ver="v0.3.0"; echo "$kTHIS_NAME $kTHIS_VERSION"$'\nFor license information and more, visit '"$kTHIS_HOMEPAGE"; exit 0
;;
-h|--help)
# Print usage information and exit.
Expand Down Expand Up @@ -122,9 +122,11 @@ getAttribByteString() {

# Outputs the specified file's RESOURCE FORK as a byte string (a hex dump that is a single-line string of pairs of hex digits, without separators or line breaks, such as "000a2c".
# IMPORTANT: Hex. digits > 9 use *lowercase* characters.
# Note: This function relies on `xxd -p <file>/..namedfork/rsrc | tr -d '\n'` rather than the conceptually equivalent `getAttributeByteString <file> com.apple.ResourceFork`
# for PERFORMANCE reasons: getAttributeByteString() relies on `xattr`, which is a *Python* script and therefore quite slow due to Python's startup cost.
# getAttribByteString <file>
# Note: This function relies on `xxd -p <file>/..namedfork/rsrc | tr -d '\n'` rather than the conceptually equivalent call,
# `getAttribByteString <file> com.apple.ResourceFork`, for PERFORMANCE reasons:
# getAttribByteString() (defined above) relies on `xattr`, which is a *Python* script [!! seemingly no longer, as of macOS 10.16]
# and therefore quite slow due to Python's startup cost.
# getResourceByteString <file>
getResourceByteString() {
xxd -p "$1"/..namedfork/rsrc | tr -d '\n'
}
Expand Down Expand Up @@ -198,13 +200,59 @@ setCustomIcon() {
# !!
# !! Note: setIcon_forFile_options_() seemingly always indicates True, even with invalid image files, so
# !! we attempt no error handling in the Python code.
/usr/bin/python - "$imgFile" "$fileOrFolder" <<'EOF' || return
import Cocoa
import sys

# macOS versions <= 12.2:
# * Use the 2.x system python, which comes with the pyobjc packages preinstalled.
if [[ -x /usr/bin/python && -z $__FILEICON_USEPY3 ]]; then

/usr/bin/python - "$imgFile" "$fileOrFolder" <<'EOF' || return
import sys, Cocoa
Cocoa.NSWorkspace.sharedWorkspace().setIcon_forFile_options_(Cocoa.NSImage.alloc().initWithContentsOfFile_(sys.argv[1].decode('utf-8')), sys.argv[2].decode('utf-8'), 0)
EOF
# macOS versions >= 12.3, where the v2.x /usr/bin/python has been removed, or
# if Python 3 is explicitly requested via a non-empty $__FILEICON_USEPY3 env. var:
# * Use the first `python3` in the $PATH, which by default is the STUB at
# /usr/bin/python3 that triggers a download-on-demand prompt as part of the
# Xcode command-line tools.
# For users that have a Homebrew 3.x Python installation, *it* will be used,
# assuming its binary comes *first* in $PATH.
else

local done=0 retrying=0 output
while (( ! done )); do
# Note: The only difference to the v2.x Python code above is the removal of the .decode('utf-8')
# calls, which are no longer necessary in v3.x.
output="$(python3 - "$imgFile" "$fileOrFolder" 2>&1 <<'EOF'
import sys, Cocoa
Cocoa.NSWorkspace.sharedWorkspace().setIcon_forFile_options_(Cocoa.NSImage.alloc().initWithContentsOfFile_(sys.argv[1]), sys.argv[2], 0)
EOF
)"
local ec=$? packageNames='pyobjc-core pyobjc-framework-Cocoa'
done=1
if (( ec )); then # The call failed.
(( retrying )) && die "An unexpected error occured: $output" # Even on-demand pyobjc installation failed -> abort.
# First, make sure that `python3` is actually Python, and not just the *stub* executable.
python3 --version | /usr/bin/grep -q ' 3.' || die "The 'python3' binary in your path is not functional, presumably because it is the preinstalled stub at /usr/bin/python3 that triggers a prompt for on-demand installation. Perform this installation, or, if you have Homebrew installed, install Python 3 with \`brew install python@3\`"
# Assume that the problem is the absence of the required pyobjc-* packages; attempt installation now.
# Tips for debugging:
# * To exercise this function, from the repo dir.:
# touch /tmp/tf; ./bin/fileicon set /tmp/tf ./test/.fixtures/img.png
# * To manage packages; prepend `sudo -H` to see machine-level packages
# pip3 list -v # shows installed packages and their locations
# pip3 uninstall -y pyobjc-core pyobjc-framework-Cocoa # uninstalls the required pyobjc-* packages
# * To manage pip3's package *cache*
# pip3 cache list # list cached packages
# pip3 cache purge # clear cache
# pip config set global.cache-dir false # disable cache globally
# pip config unset global.cache-dir # re-enable globacl cache
echo "Performing one-time user-level installation of required Python packages: $packageNames - this can take while..." >&2
pip3 install -q --user $packageNames || die "On-demand installation of Python packages failed unexpectedly."
done=0 retrying=1 # Retry
fi

done

fi

# Verify that a resource fork with icons was actually created.
# For *files*, the resource fork is embedded in the file itself.
Expand Down
5 changes: 3 additions & 2 deletions man/fileicon.1
@@ -1,4 +1,4 @@
.TH "FILEICON" "1" "December 2019" "v0.2.4" ""
.TH "FILEICON" "1" "February 2022" "v0.3.0" ""
.SH "NAME"
\fBfileicon\fR \- manage file and folder custom icons
.SH SYNOPSIS
Expand Down Expand Up @@ -54,7 +54,8 @@ to them\.
.P
\fB<imageFile>\fP can be an image file of any format supported by the system\.
.br
It is converted to an icon and assigned to \fB<fileOrFolder>\fP\|\.
It is converted to an icon and assigned to \fB<fileOrFolder>\fP\|\.
.br
If you omit \fB<imageFile>\fP, \fB<fileOrFolder>\fP must itself be an image file whose
image should become its own icon\.
.P
Expand Down

0 comments on commit be6b1de

Please sign in to comment.