Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Install executables #126

Merged
merged 3 commits into from
Apr 12, 2018
Merged

Install executables #126

merged 3 commits into from
Apr 12, 2018

Conversation

ysbaddaden
Copy link
Contributor

@ysbaddaden ysbaddaden commented Sep 23, 2016

WIP to implement #95 as per #95 (comment)

blocked by #123 and maybe #124

Copy link
Contributor

@RX14 RX14 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I realise this is a WIP PR but I thought I would note some things regardless.

src/validator.cr Outdated
end

module Validate
macro error(message)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this have to be a macro?

src/validator.cr Outdated
end

unless version =~ /\A[0-9.]+(-[-_a-z.])?\Z/
warning "version should follow semantic versioning"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mention calver?

}

with_shard(metadata) do
puts run("shards install --no-color", capture: true)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Debug puts

@ysbaddaden
Copy link
Contributor Author

@RX14 bad commit, the src/validator.cr file shouldn't have been there!

SPEC.md Outdated
A list of executables to be linked by the shared (Array).

The executables can be of any type or language (e.g., shell, binary, ruby), they
must have the executable bit set (POSIX platforms) and they must exit in the
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

exit -> exist

@ysbaddaden ysbaddaden force-pushed the feature/executables branch 3 times, most recently from d6bb426 to 4adb052 Compare September 27, 2016 20:33
@ysbaddaden ysbaddaden modified the milestones: v0.7.0, v0.8.0 Nov 17, 2016
@ysbaddaden ysbaddaden force-pushed the feature/executables branch 2 times, most recently from 5300e03 to 9460a40 Compare November 23, 2016 01:00
@ysbaddaden
Copy link
Contributor Author

ysbaddaden commented Nov 24, 2016

I'm changing my point of view, again. We should have stubs instead of copying the executables, in order to have some environments variables about the project, namely APP_ROOT, APP_NAME, SHARD_ROOT as well as a tweaked CRYSTAL_PATH. This will allow to have some information about the project, and allow to run/build crystal code on-the-fly using the project's lib folder for dependencies (for example). This should allow what @bcardiff explained in #95 (comment)

This will require a binstubs command to regenerate the stubs, for example after moving a project to another folder on the computer.

@ysbaddaden
Copy link
Contributor Author

I'm now unconvinced by binstubs 😖 I really prefer simply copying executables into the bin folder. Bonus: it's already implemented and can be merged right now.

For @bcardiff scenario: the executable can assume (or require) to be run from the project's top directory (e.g. bin/generator). That would automatically use the lib folder if calling crystal for example. If the executable is actually a crystal source code with a shebang (#! /usr/bin/env crystal) then it may require dependencies or the shard itself as a dependency (require "name/file") or directly (../lib/name/src/file). The app context could be merely Dir.current or File.dirname(Dir.executable_path); then the shard contents can be found under lib/name, etc.

@RX14
Copy link
Contributor

RX14 commented Dec 22, 2016

@ysbaddaden What are the disadvantages of binstubs? It doesn't seem like all too much more code.

@@ -44,7 +44,7 @@ module Shards
end
end

protected def install_path
def install_path

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You have to test this method if you are turning it public #justsaying

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. Writing unit tests for details would merely duplicate the integration test.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't agree. If you are creating something public you have to guarantee it is correct and the way to do that is automated tests.

But it is up to you.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an application, not a library. You may use it as a library, but at your own risk: I don't guarantee the internal API to be stable.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good to know that you don't guarantee the internal API to be stable for you or for those who one day would contribute.

Like I said: it is up to you.

veelenga added a commit to crystal-ameba/ameba that referenced this pull request Nov 16, 2017
Just a workaround before crystal-lang/shards#126
becomes live.
src/package.cr Outdated
Shards.logger.debug "Install bin/#{name}"
source = File.join(resolver.install_path, "bin", name)
dest = File.join(bin_path, name)
FileUtils.cp(source, dest)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not symlink?

Copy link
Contributor Author

@ysbaddaden ysbaddaden Feb 8, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because bin should assume to have binaries? Thus, to prepare a package for example, I can cp bin/foo dist/bin/ rather than cp $(realpath bin/foo) dist/bin/ or digging cp lib/foo/bin/foo dist/bin because bin/foo can't be relied upon?

Maybe we could hardlink?

@RX14
Copy link
Contributor

RX14 commented Feb 7, 2018

I agree with @ysbaddaden that executables should just assume they're run from the project root. Both crystal and shards assume this. Let's just merge this as-is.

@@ -14,6 +14,7 @@ module Shards
Shards.logger.info "Installing #{package.name} (#{package.version})"
package.install
end
package.install_executables
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hum, executables haven't been rebuilt if the dependency was already installed, why bother reinstalling it? Or maybe a loose version (reinstall if missing?)

@ysbaddaden ysbaddaden force-pushed the feature/executables branch 2 times, most recently from 455aaa1 to 7393ee4 Compare February 8, 2018 13:37
@ysbaddaden
Copy link
Contributor Author

Reworked. It now creates a hardlink on POSIX systems, unless the same executable was already installed (i.e. same inode). On Windows it just copies the executable.

src/package.cr Outdated
next if File.stat(destination).ino == File.stat(source).ino
File.delete(destination)
end
File.link(source, destination)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just because not being executed on windows doesn't mean the file system necessarily supports hard links. There should be a fallback in case File.link does not work.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

windows supports hard links though

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I'm blinded. To me 1. Windows is stuck on FAT32 file systems, and 2. anything else is POSIX and supports hard links (or we're talking embedded systems and they're out-of-scope).

I'll remove the Windows check and assume POSIX until someone with better win32 knowledge ports shards.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

POSIX generally supports hard links, but not every supported filesystem does. That can be an issue for example with remote file systems.

I typically use crystal in a linux VM which mounts the windows host's file system. Creating hard links is not possible on such a file system.

There are probably other issues as well but the main point is: it doesn't need to be a hard link, a symlink or copy work as well (allthough with minor drawbacsk). Hard links are not 100% supported everywhere, so there should be an alternative as fallback.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sigh.

src/package.cr Outdated
FileUtils.cp(source, destination)
{% else %}
if File.exists?(destination)
next if File.stat(destination).ino == File.stat(source).ino
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd just take this out, and either:

  • always replace
  • always ignore if it exists.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can't ignore: updating a shard may build a new executable, if shards made the hardlink, we're all set (no need to copy), but if the destination was changed for some reason, then we should delete/link again.

Copy link
Contributor

@RX14 RX14 Feb 8, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well inodes are going to be removed from File::Info because they're platform-specific so there's little point in having this check just to remove it later.

Let's just always try hardlinking, and on an exception try copying. It works on every platform and is simple.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sigh.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's just make File::Info have mtime, file? and directory?. Let's drop everything else. Not cross platform, useless or whatever else. I'll use stat(2) directly to get anything done 😭

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well:

File.link("foo", "bar") # => 0
File.link("foo", "bar") # => Error creating link from foo to bar: File exists (Errno)

The only solutions are:

  • delete/copy all the time (stupid, waste);
  • check inodes, delete + link if they're different (smart, efficient).

I'm sorry, but I won't remove the inode check. You'll have to add File::Info#inode for systems that support it, so we have a compile time error on windows, for example.

Copy link
Contributor

@RX14 RX14 Feb 9, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, the correct solution is to hide the inode and supply a File::Info#same?(File::Info). On unix it will check if device and inode are equal (but those fields are still hidden). On windows it does whatever. I suppose I should implement this.

src/package.cr Outdated
File.link(source, destination)
{% end %}
rescue Errno : ex
if ex.errno == Errno::EPERM
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it hurt to try regardless of the error? Stuff like EXDEV... (extremely unlikely)

@RX14
Copy link
Contributor

RX14 commented Feb 9, 2018

Apart from the above incredibly tiny nitpick i'm 👍 to merge.

@plainas
Copy link

plainas commented Feb 16, 2018

OK, the discussion has branched quite a bit since 'executables' was mentioned for the first time.

Personally, I think a simple way of installing binaries is of primary importance. Crystal programs, being, by and large, dependency free self contained libraries, are a perfect fit for small command line utilities.

Being able to run crystal init, write some code, shards install and have a command line tool on the system, is of tremendous convenience. How far is this feature from becoming a reality?

Is #124 still blocking this?

@RX14
Copy link
Contributor

RX14 commented Mar 30, 2018

Would be nice to get this in for next release. Could help avoid things like rails cli being global in crystal.

Uses (hard) links on POSIX systems to install the executable, taking
care to verify whether the destination isn't already the source
(i.e. same inode). On Windows we just copy the file.

closes #95
Some filesystems don't support hard links which is reported with
the EPERM errno, or we try to link across mounted file systems.
These cases are edge cases, but let's avoid errors and merely copy
the executable.
@ysbaddaden ysbaddaden merged commit 2491f80 into master Apr 12, 2018
@ysbaddaden ysbaddaden deleted the feature/executables branch April 12, 2018 09:14
@Sija
Copy link
Contributor

Sija commented Apr 12, 2018

What's the flow of installing executables specified in targets?
Would below code work?

# shard.yml

# ...

targets:
  foo:
    main: src/cli.cr

scripts:
  postinstall: shards build --no-debug

executables:
  - foo

Great to see this merged btw! 🎉

@ysbaddaden
Copy link
Contributor Author

Yes, but transitive dependencies will prevent building your executable. See #124.

@ysbaddaden
Copy link
Contributor Author

BTW: don't use --no-debug. You're only shaving a few kilobytes —we only generate file:line info by default— and make it damn harder to debug exceptions. You should use --release thought.

taylor pushed a commit to vulk/shards that referenced this pull request Aug 11, 2020
Uses (hard) links on POSIX systems to install the executable,
taking care to verify whether the destination isn't already the
source (i.e. same inode). On Windows we just copy the file.

Some filesystems don't support hard links which is reported with
the EPERM errno, or we try to link across mounted file systems.
These cases are edge cases, but we fallback to copy the
executable to avoid errors.
f-fr pushed a commit to f-fr/shards that referenced this pull request Jan 2, 2021
Uses (hard) links on POSIX systems to install the executable,
taking care to verify whether the destination isn't already the
source (i.e. same inode). On Windows we just copy the file.

Some filesystems don't support hard links which is reported with
the EPERM errno, or we try to link across mounted file systems.
These cases are edge cases, but we fallback to copy the
executable to avoid errors.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants