Ln.scala 4.34 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
 * Biopet is built on top of GATK Queue for building bioinformatic
 * pipelines. It is mainly intended to support LUMC SHARK cluster which is running
 * SGE. But other types of HPC that are supported by GATK Queue (such as PBS)
 * should also be able to execute Biopet tools and pipelines.
 *
 * Copyright 2014 Sequencing Analysis Support Core - Leiden University Medical Center
 *
 * Contact us at: sasc@lumc.nl
 *
 * A dual licensing mode is applied. The source code within this project that are
 * not part of GATK Queue is freely available for non-commercial use under an AGPL
 * license; For commercial users or users who do not want to follow the AGPL
 * license, please contact us to obtain a separate license.
 */
16
package nl.lumc.sasc.biopet.extensions
Peter van 't Hof's avatar
Peter van 't Hof committed
17
18

import java.io.File
Peter van 't Hof's avatar
Peter van 't Hof committed
19

Peter van 't Hof's avatar
Peter van 't Hof committed
20
import nl.lumc.sasc.biopet.utils.config.Configurable
21
22
import org.broadinstitute.gatk.queue.function.InProcessFunction
import org.broadinstitute.gatk.utils.commandline.{ Input, Output }
Peter van 't Hof's avatar
Peter van 't Hof committed
23
24

import scala.sys.process.{ Process, ProcessLogger }
Peter van 't Hof's avatar
Peter van 't Hof committed
25

Peter van 't Hof's avatar
Peter van 't Hof committed
26
27
28
/**
 * This class can execute ln as InProcessFunction or used to only generate the ln command
 */
bow's avatar
bow committed
29
class Ln(val root: Configurable) extends InProcessFunction with Configurable {
Peter van 't Hof's avatar
Peter van 't Hof committed
30
  this.analysisName = getClass.getSimpleName
bow's avatar
bow committed
31
32

  @Input(doc = "Input file")
33
  var input: File = _
bow's avatar
bow committed
34
35

  @Output(doc = "Link destination")
36
  var output: File = _
bow's avatar
bow committed
37

Peter van 't Hof's avatar
Peter van 't Hof committed
38
  @Input(required = false)
Peter van 't Hof's avatar
Peter van 't Hof committed
39
40
  var deps: List[File] = Nil

41
42
  var relative: Boolean = true

Peter van 't Hof's avatar
Peter van 't Hof committed
43
  /** Generate out file for job */
44
  override def freezeFieldValues(): Unit = {
45
46
    val outLog: String = ".%s.%s.out".format(output.getName, analysisName)
    jobOutputFile = new File(output.getAbsoluteFile.getParentFile, outLog)
47
48
49
    super.freezeFieldValues()
  }

Peter van 't Hof's avatar
Peter van 't Hof committed
50
  /** return commandline to execute */
51
52
53
  lazy val cmd: String = {
    lazy val inCanonical: String = {
      // need to remove "/~" to correctly expand path with tilde
54
      input.getAbsolutePath.replace("/~", "")
55
    }
56

57
    lazy val outCanonical: String = output.getAbsolutePath.replace("/~", "")
58

59
    lazy val inToks: Array[String] = inCanonical.split(File.separator)
60

61
    lazy val outToks: Array[String] = outCanonical.split(File.separator)
62

63
64
    lazy val commonPrefixLength: Int = {
      val maxLength = scala.math.min(inToks.length, outToks.length)
Peter van 't Hof's avatar
Peter van 't Hof committed
65
66
      var i: Int = 0
      while (i < maxLength && inToks(i) == outToks(i)) i += 1
67
68
      i
    }
69

70
71
72
    lazy val inUnique: String = {
      inToks.slice(commonPrefixLength, inToks.length).mkString(File.separator)
    }
73

74
75
76
    lazy val outUnique: String = {
      outToks.slice(commonPrefixLength, outToks.length).mkString(File.separator)
    }
77

78
79
80
    lazy val inRelative: String = {
      // calculate 'distance' from output directory to input
      // which is the number of directory walks required to get to the inUnique directory from outDir
bow's avatar
bow committed
81
82
83
84
85
86
87
      val dist =
        // relative path differs depending on which of the input or target is in the 'higher' directory
        if (inToks.length > outToks.length)
          scala.math.max(0, inUnique.split(File.separator).length - 1)
        else
          scala.math.max(0, outUnique.split(File.separator).length - 1)

88
      val result =
bow's avatar
bow committed
89
        if (dist == 0 || inToks.length > outToks.length)
90
          inUnique
bow's avatar
bow committed
91
92
        else
          ((".." + File.separator) * dist) + inUnique
bow's avatar
bow committed
93

94
95
      result
    }
96

97
98
    if (relative) {
      // workaround until we have `ln` that works with relative path (i.e. `ln -r`)
99
      "ln -s " + inRelative + " " + outCanonical
100
    } else {
101
      "ln -s " + inCanonical + " " + outCanonical
102
103
104
    }
  }

Peter van 't Hof's avatar
Peter van 't Hof committed
105
  override def run() {
106
107
108
    val stdout = new StringBuffer()
    val stderr = new StringBuffer()
    val process = Process(cmd).run(ProcessLogger(stdout append _ + "\n", stderr append _ + "\n"))
Peter van 't Hof's avatar
Peter van 't Hof committed
109
    val exitcode = process.exitValue()
110
111
112
113
    if (exitcode != 0) {
      throw new Exception("Error creating symbolic link, this was the original message: \n" + stderr)
    }
    logger.info("cmd: '" + cmd + "', exitcode: " + exitcode)
Peter van 't Hof's avatar
Peter van 't Hof committed
114
  }
Peter van 't Hof's avatar
Peter van 't Hof committed
115
}
116

Peter van 't Hof's avatar
Peter van 't Hof committed
117
/** Object for constructors for ln */
118
object Ln {
Peter van 't Hof's avatar
Peter van 't Hof committed
119
120
121
122
123
124
125
126
127
  /**
   * Basis constructor
   * @param root root object for config
   * @param input list of files to use
   * @param output output File
   * @param relative make reletive links (default true)
   * @return
   */

128
129
  def apply(root: Configurable, input: File, output: File, relative: Boolean = true): Ln = {
    val ln = new Ln(root)
130
131
    ln.input = input
    ln.output = output
132
    ln.relative = relative
Peter van 't Hof's avatar
Peter van 't Hof committed
133
    ln
134
135
  }
}