summaryrefslogblamecommitdiff
path: root/core/src/mill/main/Resolve.scala
blob: 1932c24140706fb70e4be63af99b99a7d040073e (plain) (tree)
1
2
3
4
5
6
7
8
9

                 

                             

                                  

                
                                                     
                                     
                                          

                                                                
                                                                                              

                            

                                                                                          
                    
             
                         
                               
                                  
                          
 







                                                               
                                                                   

                                                  

                               
                                                



                                                      
                                                     





                                                              
 

         
                             

                                                                     
                             
                                                                                                           



                          
                                                         
 
                                                                         

                                                                                  
           

                                                                            
                                                       





                                                            
                                            
                                      
 
                            
                                        




                                                                                                       








                                                                                                       







                                                                                                                                    

             
                                      

                                 
















                                                                                                                                   
 






                                                                                                                                


                                                                                                        





                                                  
package mill.main

import mill.define._
import mill.define.TaskModule
import mill.main.Router.EntryPoint
import ammonite.util.{Res}

object Resolve {
  def resolve[T, V](remainingSelector: List[Segment],
                    obj: mill.Module,
                    discover: Discover[_],
                    rest: Seq[String],
                    remainingCrossSelectors: List[List[String]],
                    revSelectorsSoFar: List[Segment]): Either[String, Seq[NamedTask[Any]]] = {

    remainingSelector match{
      case Segment.Cross(_) :: Nil => Left("Selector cannot start with a [cross] segment")
      case Segment.Label(last) :: Nil =>
        val target =
          obj
            .millInternal
            .reflect[Target[_]]
            .find(_.label == last)
            .map(Right(_))

        def shimArgsig[T](a: mill.main.Router.ArgSig[T, _]) = {
          ammonite.main.Router.ArgSig[T](
            a.name,
            a.typeString,
            a.doc,
            a.default
          )
        }
        def invokeCommand(target: mill.Module, name: String) = for{
          (cls, entryPoints) <- discover.value
          if cls.isAssignableFrom(target.getClass)
          ep <- entryPoints
          if ep._2.name == name
        } yield mill.main.Scripts.runMainMethod(
          target,
          ep._2.asInstanceOf[EntryPoint[mill.Module]],
          ammonite.main.Scripts.groupArgs(rest.toList)
        ) match{
          case Res.Success(v: Command[_]) => Right(v)
          case Res.Failure(msg) => Left(msg)
          case Res.Exception(ex, msg) =>
            val sw = new java.io.StringWriter()
            ex.printStackTrace(new java.io.PrintWriter(sw))
            val prefix = if (msg.nonEmpty) msg + "\n" else msg
            Left(prefix + sw.toString)

        }

        val runDefault = for{
          child <- obj.millInternal.reflectNestedObjects[mill.Module]
          if child.millOuterCtx.segment == Segment.Label(last)
          res <- child match{
            case taskMod: TaskModule => Some(invokeCommand(child, taskMod.defaultCommandName()).headOption)
            case _ => None
          }
        } yield res

        val command = invokeCommand(obj, last).headOption

        command orElse target orElse runDefault.flatten.headOption match{
          case None =>  Left("Cannot resolve task " +
            Segments((Segment.Label(last) :: revSelectorsSoFar).reverse:_*).render
          )
          // Contents of `either` *must* be a `Task`, because we only select
          // methods returning `Task` in the discovery process
          case Some(either) => either.right.map(Seq(_))
        }


      case head :: tail =>
        val newRevSelectorsSoFar = head :: revSelectorsSoFar
        head match{
          case Segment.Label(singleLabel) =>
            if (singleLabel == "__") {

              val matching =
                obj.millInternal.modules
                  .map(resolve(tail, _, discover, rest, remainingCrossSelectors, newRevSelectorsSoFar))
                  .collect{case Right(vs) => vs}.flatten

              if (matching.nonEmpty)Right(matching)
              else Left("Cannot resolve module " + Segments(newRevSelectorsSoFar.reverse:_*).render)
            } else if (singleLabel == "_") {

              val matching =
                obj.millModuleDirectChildren
                  .map(resolve(tail, _, discover, rest, remainingCrossSelectors, newRevSelectorsSoFar))
                  .collect{case Right(vs) => vs}.flatten

              if (matching.nonEmpty)Right(matching)
              else Left("Cannot resolve module " + Segments(newRevSelectorsSoFar.reverse:_*).render)
            }else{

              obj.millInternal.reflectNestedObjects[mill.Module].find{
                _.millOuterCtx.segment == Segment.Label(singleLabel)
              } match{
                case Some(child: mill.Module) => resolve(tail, child, discover, rest, remainingCrossSelectors, newRevSelectorsSoFar)
                case None => Left("Cannot resolve module " + Segments(newRevSelectorsSoFar.reverse:_*).render)
              }
            }

          case Segment.Cross(cross) =>
            obj match{
              case c: Cross[_] =>
                if(cross == Seq("__")){
                  val matching =
                    for ((k, v) <- c.items)
                    yield resolve(tail, v.asInstanceOf[mill.Module], discover, rest, remainingCrossSelectors, newRevSelectorsSoFar)

                  val results = matching.collect{case Right(res) => res}.flatten

                  if (results.isEmpty) Left("Cannot resolve cross " + Segments(newRevSelectorsSoFar.reverse:_*).render)
                  else Right(results)
                } else if (cross.contains("_")){
                  val matching = for {
                    (k, v) <- c.items
                    if k.length == cross.length
                    if k.zip(cross).forall { case (l, r) => l == r || r == "_" }
                  } yield resolve(tail, v.asInstanceOf[mill.Module], discover, rest, remainingCrossSelectors, newRevSelectorsSoFar)

                  val results = matching.collect{case Right(res) => res}.flatten

                  if (results.isEmpty) Left("Cannot resolve cross " + Segments(newRevSelectorsSoFar.reverse:_*).render)
                  else Right(results)
                }else{
                  c.itemMap.get(cross.toList) match{
                    case Some(m: mill.Module) => resolve(tail, m, discover, rest, remainingCrossSelectors, newRevSelectorsSoFar)
                    case None => Left("Cannot resolve cross " + Segments(newRevSelectorsSoFar.reverse:_*).render)
                  }
                }
              case _ => Left("Cannot resolve cross " + Segments(newRevSelectorsSoFar.reverse:_*).render)
            }
        }

      case Nil => Left("Selector cannot be empty")
    }
  }
}