Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

add support for linked folders in Eclipse #124

Closed
wants to merge 5 commits into from

3 participants

@earldouglas

Eclipse has support for including code from folders external to a project directory, by creating a "linked folder" and adding a classpath entry relative to that linked folder. For example, the following Eclipse configuration will allow usage of /path/to/alternate/location/sources/common/src/main/java as an Eclipse source folder:

.project:

<linkedResources>
  <link>
    <name>src-common</name>
    <type>2</type>
    <location>/path/to/alternate/location/sources/common</location>
  </link>
</linkedResources>

.classpath:

<classpathentry output="target/classes" path="src-common/main/java" kind="src"></classpathentry>

The current version of sbteclipse blows up when trying to handle external folders such as this, because they can't be relativized to the current project directory. This pull request adds support for defining linked folders, against which sbteclipse will attempt to map external source paths.

To use this feature, the new setting linkedFolders is used in conjunction with external unmanaged source paths:

EclipseKeys.linkedFolders := Seq( ("src-common", file("/") / "ext-path" / "java-common") ),
unmanagedSourceDirectories in Compile ++= Seq( file("/") / "ext-path" / "java-common" / "src" / "main" / "java" )
...main/scala/com/typesafe/sbteclipse/core/Eclipse.scala
((5 lines not shown))
classDirectory: File,
state: State): IO[EclipseClasspathEntry.Src] =
io {
if (!srcDirectory.exists()) srcDirectory.mkdirs()
EclipseClasspathEntry.Src(
- relativize(baseDirectory, srcDirectory),
+ findInLinkedFolder(srcDirectory, linkedFolders).getOrElse(relativize(baseDirectory, srcDirectory)),
@hseeberger Owner

Please use infix notation, i.e. no dots and parens

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
.../scala/com/typesafe/sbteclipse/core/EclipseOpts.scala
@@ -22,6 +22,8 @@ private object EclipseOpts {
val ExecutionEnvironment = "execution-environment"
+ val LinkedFolders = "linked-sources"
@hseeberger Owner

Please use consistent names: Either change the name to LinkesSources or the value to linked-folders. If I understand this Eclipse feature correctly, I think you should change the value to linked-folders

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
...main/scala/com/typesafe/sbteclipse/core/Eclipse.scala
@@ -218,6 +221,21 @@ private object Eclipse {
<natures>
{ builderAndNatures._2.map(n => <nature>{ n }</nature>) }
</natures>
+ {
+ if (!linkedFolders.isEmpty) {
+ <linkedResources>
+ {
+ linkedFolders.map { lf =>
@hseeberger Owner

Please use infix notation. Also, please use a speaking name, i.e. instead of lf use folder or linkedFolder.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@earldouglas

Thanks for the tips, Heiko! I made the changes as suggested.

earldouglas added some commits
@earldouglas earldouglas use infix notation
2841717
@earldouglas earldouglas use consistent naming for LinkedFolders/'linked-folders'
7aa69b3
@earldouglas earldouglas fix a path-related bug on Windows
Under Windows, when Eclipse imports a project it will replace
'C:\foo\bar\baz' with 'foo/bar/baz', dropping the 'C:\' and
replacing backslashes with forward slashes.  This causes
a problem in Eclipse, because it can't resolve 'foo/bar/baz'
as being on the C drive.  This fix adds an explicit replaceAll
from backslashes to forward slashes, which prevents Eclipse
from doing it wrongly.
08d54a5
@jsuereth
Owner

Hey, I had an alternative take on this I added here: #156

This one is designed to "just work" without user configuration at all. Would love to get thoughts.

@jsuereth
Owner

Also, I was unaware of this PR... sorry!

@jsuereth
Owner

Hey @JamesEarlDouglas I think we're going to opt for the fix that requries no project-specific configuration for usage (keeping the plugin usable at a user level).

Would you want to add support back for explicit configuration? If not, I'll close this in a bit.

@earldouglas

Would you want to add support back for explicit configuration?

Nah. I no longer have a use case for this, so I can't say whether it's still relevant given #156. Let's go ahead and close this one.

@jsuereth jsuereth closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jul 21, 2012
  1. @earldouglas
Commits on Jul 23, 2012
  1. @earldouglas
  2. @earldouglas

    use infix notation

    earldouglas authored
  3. @earldouglas
Commits on Jul 24, 2012
  1. @earldouglas

    fix a path-related bug on Windows

    earldouglas authored
    Under Windows, when Eclipse imports a project it will replace
    'C:\foo\bar\baz' with 'foo/bar/baz', dropping the 'C:\' and
    replacing backslashes with forward slashes.  This causes
    a problem in Eclipse, because it can't resolve 'foo/bar/baz'
    as being on the C drive.  This fix adds an explicit replaceAll
    from backslashes to forward slashes, which prevents Eclipse
    from doing it wrongly.
This page is out of date. Refresh to see the latest.
View
40 sbteclipse-core/src/main/scala/com/typesafe/sbteclipse/core/Eclipse.scala
@@ -130,6 +130,7 @@ private object Eclipse {
handleProject(
jreContainer(executionEnvironmentArg orElse executionEnvironment(ref, state)),
preTasks(ref, state),
+ linkedFolders(ref, state),
relativizeLibs(ref, state),
builderAndNatures(projectFlavor(ref, state)),
state
@@ -171,6 +172,7 @@ private object Eclipse {
def handleProject(
jreContainer: String,
preTasks: Seq[(TaskKey[_], ProjectRef)],
+ linkedFolders: Seq[(String, File)],
relativizeLibs: Boolean,
builderAndNatures: (String, Seq[String]),
state: State)(
@@ -187,13 +189,14 @@ private object Eclipse {
for {
_ <- executePreTasks(preTasks, state)
n <- io(name)
- _ <- saveXml(baseDirectory / ".project", new RuleTransformer(projectTransformers: _*)(projectXml(name, builderAndNatures)))
+ _ <- saveXml(baseDirectory / ".project", new RuleTransformer(projectTransformers: _*)(projectXml(name, builderAndNatures, linkedFolders)))
cp <- classpath(
classpathEntryTransformer,
buildDirectory,
baseDirectory,
relativizeLibs,
srcDirectories,
+ linkedFolders,
externalDependencies,
projectDependencies,
jreContainer,
@@ -207,7 +210,7 @@ private object Eclipse {
def executePreTasks(preTasks: Seq[(TaskKey[_], ProjectRef)], state: State): IO[Unit] =
io(for ((preTask, ref) <- preTasks) evaluateTask(preTask, ref, state))
- def projectXml(name: String, builderAndNatures: (String, Seq[String])): Node =
+ def projectXml(name: String, builderAndNatures: (String, Seq[String]), linkedFolders: Seq[(String, File)]): Node =
<projectDescription>
<name>{ name }</name>
<buildSpec>
@@ -218,6 +221,22 @@ private object Eclipse {
<natures>
{ builderAndNatures._2.map(n => <nature>{ n }</nature>) }
</natures>
+ {
+ if (!linkedFolders.isEmpty) {
+ <linkedResources>
+ {
+ linkedFolders.map {
+ case (name, file) =>
+ <link>
+ <name>{ name }</name>
+ <type>2</type>
+ <location>{ file.getCanonicalPath.replaceAll("\\\\", "/") }</location>
+ </link>
+ }
+ }
+ </linkedResources>
+ }
+ }
</projectDescription>
def classpath(
@@ -226,12 +245,13 @@ private object Eclipse {
baseDirectory: File,
relativizeLibs: Boolean,
srcDirectories: Seq[(File, File)],
+ linkedFolders: Seq[(String, File)],
externalDependencies: Seq[Lib],
projectDependencies: Seq[String],
jreContainer: String,
state: State): IO[Node] = {
val srcEntriesIoSeq =
- for ((dir, output) <- srcDirectories) yield srcEntry(baseDirectory, dir, output, state)
+ for ((dir, output) <- srcDirectories) yield srcEntry(baseDirectory, dir, linkedFolders, output, state)
for (srcEntries <- srcEntriesIoSeq.sequence) yield {
val entries = srcEntries ++
(externalDependencies map libEntry(buildDirectory, baseDirectory, relativizeLibs, state)) ++
@@ -245,16 +265,25 @@ private object Eclipse {
def srcEntry(
baseDirectory: File,
srcDirectory: File,
+ linkedFolders: Seq[(String, File)],
classDirectory: File,
state: State): IO[EclipseClasspathEntry.Src] =
io {
if (!srcDirectory.exists()) srcDirectory.mkdirs()
EclipseClasspathEntry.Src(
- relativize(baseDirectory, srcDirectory),
+ findInLinkedFolder(srcDirectory, linkedFolders) getOrElse relativize(baseDirectory, srcDirectory),
relativize(baseDirectory, classDirectory)
)
}
+ def findInLinkedFolder(srcDirectory: File, linkedFolders: Seq[(String, File)]): Option[String] = {
+ val paths = for {
+ (name, path) <- linkedFolders
+ rel <- IO.relativize(path, srcDirectory)
+ } yield (name + "/" + rel)
+ paths.headOption
+ }
+
def libEntry(
buildDirectory: File,
baseDirectory: File,
@@ -423,6 +452,9 @@ private object Eclipse {
def executionEnvironment(ref: Reference, state: State): Option[EclipseExecutionEnvironment.Value] =
setting(EclipseKeys.executionEnvironment in ref, state).fold(_ => None, id)
+ def linkedFolders(ref: Reference, state: State): Seq[(String, File)] =
+ setting(EclipseKeys.linkedFolders in ref, state).fold(_ => Nil, id)
+
def skipParents(ref: Reference, state: State): Boolean =
setting(EclipseKeys.skipParents in ref, state).fold(_ => true, id)
View
2  sbteclipse-core/src/main/scala/com/typesafe/sbteclipse/core/EclipseOpts.scala
@@ -22,6 +22,8 @@ private object EclipseOpts {
val ExecutionEnvironment = "execution-environment"
+ val LinkedFolders = "linked-folders"
+
val SkipParents = "skip-parents"
val WithSource = "with-source"
View
5 sbteclipse-core/src/main/scala/com/typesafe/sbteclipse/core/EclipsePlugin.scala
@@ -55,6 +55,11 @@ trait EclipsePlugin {
"The optional Eclipse execution environment."
)
+ val linkedFolders: SettingKey[Seq[(String, File)]] = SettingKey(
+ prefix(LinkedFolders),
+ "Link external folders."
+ )
+
val skipParents: SettingKey[Boolean] = SettingKey(
prefix(SkipParents),
"Skip creating Eclipse files for parent project?"
View
11 sbteclipse-plugin/src/sbt-test/sbteclipse/02-contents/build.sbt
@@ -31,6 +31,17 @@ TaskKey[Unit]("verify-project-xml-java") <<= baseDirectory map { dir =>
// java project nature
verify("buildCommand", "org.eclipse.jdt.core.javabuilder", (projectDescription \ "buildSpec" \ "buildCommand" \ "name").text)
verify("natures", "org.eclipse.jdt.core.javanature", (projectDescription \ "natures" \ "nature").text)
+ // linked resources
+ verify("linkedResources", "src-common", (projectDescription \ "linkedResources" \ "link" \ "name").text)
+ verify("linkedResources", "2", (projectDescription \ "linkedResources" \ "link" \ "type").text)
+ if (!(projectDescription \ "linkedResources" \ "link" \ "location").text.endsWith("/java-common"))
+ error("""Expected .project of Java project to contain a linkedResources link with location ending in "/java-common" """)
+}
+
+TaskKey[Unit]("verify-classpath-xml-java") <<= baseDirectory map { dir =>
+ val classpath = XML.loadFile(dir / "java" / ".classpath")
+ if (!(classpath.child contains <classpathentry kind="src" path="src-common/main/java" output="target/scala-2.9.2/classes" />))
+ error("""Expected .classpath of Java project to contain <classpathentry kind="src" path="src-common/main/java" output="target/scala-2.9.2/classes" /> """)
}
TaskKey[Unit]("verify-project-xml-subd") <<= baseDirectory map { dir =>
View
4 sbteclipse-plugin/src/sbt-test/sbteclipse/02-contents/project/Build.scala
@@ -116,7 +116,9 @@ object Build extends Build {
"java",
new File("java"),
settings = Project.defaultSettings ++ Seq(
- EclipseKeys.projectFlavor := EclipseProjectFlavor.Java
+ EclipseKeys.projectFlavor := EclipseProjectFlavor.Java,
+ EclipseKeys.linkedFolders := Seq( ("src-common", file(".") / "java-common") ),
+ unmanagedSourceDirectories in Compile ++= Seq( file(".") / "java-common" / "src" / "main" / "java" )
)
)
Something went wrong with that request. Please try again.