-
Notifications
You must be signed in to change notification settings - Fork 68
#1788: Created a commandlet to simulate the behaviour of ln -s #1847
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
base: main
Are you sure you want to change the base?
Changes from 29 commits
7cf83da
818f7a4
d9d1101
e1182c0
0e14ba4
61a560d
d0e5bf9
46236f2
3116b1c
ecabdb9
4d9db9a
8da6d74
aa91650
7f733f8
1221f3c
568d3ed
88df80b
cf7dc41
00fb7df
bd3a739
a031666
197fc07
fdac6b9
77b001d
dfe6211
2503d34
1140604
010935d
56c577b
9a4a38a
5e23c49
177aa64
cdadf35
2f60435
9e342cd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -14,7 +14,6 @@ | |
| !.anyedit.properties | ||
| !.ide.properties | ||
| !.templateengine | ||
| !.github | ||
| target/ | ||
| eclipse-target/ | ||
| generated/ | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,91 @@ | ||
| package com.devonfw.tools.ide.commandlet; | ||
|
|
||
| import java.nio.file.Files; | ||
| import java.nio.file.Path; | ||
|
|
||
| import com.devonfw.tools.ide.cli.CliException; | ||
| import com.devonfw.tools.ide.context.IdeContext; | ||
| import com.devonfw.tools.ide.io.PathLinkType; | ||
| import com.devonfw.tools.ide.property.FlagProperty; | ||
| import com.devonfw.tools.ide.property.PathProperty; | ||
|
|
||
| /** | ||
| * // * Link creation {@link Commandlet} similar to {@code ln -s}. | ||
| * <p> | ||
| * It tries to create a true symbolic link first. On Windows, symlink creation may be restricted due to missing privileges. In that case, IDEasy will create a | ||
| * hard link as an alternative (file-only, same volume) to avoid the Git-Bash behavior of silently copying files. | ||
| */ | ||
| public final class LnCommandlet extends Commandlet { | ||
|
|
||
| /** Grammar token {@code -s} (optional). */ | ||
| public final FlagProperty symbolic; | ||
|
|
||
| /** Grammar token {@code -r} (optional). */ | ||
| public final FlagProperty relative; | ||
|
|
||
| /** The source path to link to. */ | ||
| public final PathProperty source; | ||
|
|
||
| /** The target path (the created link). */ | ||
| public final PathProperty link; | ||
|
|
||
| /** | ||
| * The constructor. | ||
| * | ||
| * @param context the {@link IdeContext}. | ||
| */ | ||
| public LnCommandlet(IdeContext context) { | ||
|
|
||
| super(context); | ||
| addKeyword(getName()); | ||
|
|
||
| this.symbolic = add(new FlagProperty("--symbolic", false, "-s")); | ||
| this.relative = add(new FlagProperty("--relative", false, "-r")); | ||
| this.source = add(new PathProperty("", true, "source", true)); | ||
| this.link = add(new PathProperty("", true, "link", true)); | ||
| } | ||
|
|
||
| @Override | ||
| public String getName() { | ||
|
|
||
| return "ln"; | ||
| } | ||
|
|
||
| @Override | ||
| public boolean isIdeRootRequired() { | ||
|
|
||
| return false; | ||
| } | ||
|
|
||
| @Override | ||
| public boolean isIdeHomeRequired() { | ||
|
|
||
| return false; | ||
| } | ||
|
|
||
| @Override | ||
| public boolean isWriteLogFile() { | ||
|
|
||
| return false; | ||
| } | ||
|
|
||
| @Override | ||
| protected void doRun() { | ||
|
|
||
| Path cwd = this.context.getCwd(); | ||
| if (cwd == null) { | ||
| throw new CliException("Missing current working directory!"); | ||
| } | ||
|
|
||
| Path sourcePath = cwd.resolve(this.source.getValue()).normalize(); | ||
| Path linkPath = cwd.resolve(this.link.getValue()).normalize(); | ||
| boolean relative = this.relative.isTrue(); | ||
|
|
||
| if (!Files.exists(sourcePath)) { | ||
| throw new CliException("Source does not exist: " + sourcePath); | ||
| } | ||
|
|
||
| PathLinkType linkType = this.symbolic.isTrue() ? PathLinkType.SYMBOLIC_LINK : PathLinkType.HARD_LINK; | ||
| this.context.getFileAccess().link(sourcePath, linkPath, relative, linkType, this.context.isForceMode()); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -527,7 +527,7 @@ private boolean runMklink(Path source, Path link, Path cwd, String option) { | |
| } | ||
|
|
||
| @Override | ||
| public void link(Path source, Path link, boolean relative, PathLinkType type) { | ||
| public void link(Path source, Path link, boolean relative, PathLinkType type, boolean override) { | ||
|
|
||
| Path finalLink = link.toAbsolutePath().normalize(); | ||
| Path finalSource; | ||
|
|
@@ -539,13 +539,15 @@ public void link(Path source, Path link, boolean relative, PathLinkType type) { | |
| Path absoluteSource = finalSource.isAbsolute() ? finalSource : finalLink.getParent().resolve(finalSource).normalize(); | ||
| String relativeOrAbsolute = relative ? "relative" : "absolute"; | ||
| LOG.debug("Creating {} {} at {} pointing to {}", relativeOrAbsolute, type, finalLink, finalSource); | ||
| deleteLinkIfExists(finalLink); | ||
| if (override) { | ||
| deleteLinkIfExists(finalLink); | ||
| } | ||
| try { | ||
| // Attention: JavaDoc and position of path arguments can be very confusing - see comment in #1736 | ||
| if (type == PathLinkType.SYMBOLIC_LINK) { | ||
| Files.createSymbolicLink(finalLink, finalSource); | ||
| } else if (type == PathLinkType.HARD_LINK) { | ||
| Files.createLink(finalLink, finalSource); | ||
|
hohwille marked this conversation as resolved.
|
||
| createHardLink(finalSource, finalLink, override); | ||
| } else { | ||
| throw new IllegalStateException("" + type); | ||
| } | ||
|
|
@@ -555,7 +557,13 @@ public void link(Path source, Path link, boolean relative, PathLinkType type) { | |
| "Due to lack of permissions, Microsoft's mklink with junction had to be used to create a Symlink. See\n" | ||
| + "https://github.com/devonfw/IDEasy/blob/main/documentation/symlink.adoc for further details. Error was: " | ||
| + e.getMessage()); | ||
| mklinkOnWindows(finalSource, absoluteSource, finalLink, type, relative); | ||
|
|
||
| try { | ||
| mklinkOnWindows(finalSource, absoluteSource, finalLink, type, relative); | ||
| } catch (IllegalStateException mkEx) { | ||
| LOG.info("Creating a hard link as a fallback for the failed mklink attempt."); | ||
| createHardLink(absoluteSource, finalLink, override); | ||
| } | ||
|
hohwille marked this conversation as resolved.
|
||
| } else { | ||
| throw new RuntimeException(e); | ||
| } | ||
|
|
@@ -564,6 +572,26 @@ public void link(Path source, Path link, boolean relative, PathLinkType type) { | |
| } | ||
| } | ||
|
|
||
|
|
||
| /** | ||
| * Creates a hard link at {@code link} pointing to {@code source}. | ||
| * | ||
| * @param source the {@link Path} the hard link will point to. | ||
| * @param link the {@link Path} where to create the hard link. | ||
| * @param override - {@code true} to override existing links, {@code false} otherwise. | ||
| */ | ||
| void createHardLink(Path source, Path link, boolean override) { | ||
| if (override && Files.exists(link, LinkOption.NOFOLLOW_LINKS)) { | ||
| delete(link); | ||
| } | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would still expect this to be done also for symbolic links.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for the suggestions, implemented the suggested changes ! |
||
| try { | ||
| Files.createLink(link, source); | ||
| LOG.trace("Created hard link at {} pointing to {}", link, source); | ||
| } catch (IOException e) { | ||
| throw new RuntimeException("Failed to create a hardlink for " + source + " at " + link, e); | ||
| } | ||
| } | ||
|
|
||
| @Override | ||
| public Path getPathStart(Path path, int nameEnd) { | ||
|
|
||
|
|
||
|
KarimALotfy marked this conversation as resolved.
|
|
KarimALotfy marked this conversation as resolved.
|
Uh oh!
There was an error while loading. Please reload this page.