mirror of https://github.com/sbt/sbt.git
Merge pull request #5439 from dwijnand/introduce-Taskable
Introduce Taskable & toTaskable
This commit is contained in:
commit
0b12862caf
|
|
@ -805,6 +805,9 @@ lazy val mainSettingsProj = (project in file("main-settings"))
|
||||||
// added a method to a sealed trait
|
// added a method to a sealed trait
|
||||||
exclude[InheritedNewAbstractMethodProblem]("sbt.Scoped.canEqual"),
|
exclude[InheritedNewAbstractMethodProblem]("sbt.Scoped.canEqual"),
|
||||||
exclude[InheritedNewAbstractMethodProblem]("sbt.ScopedTaskable.canEqual"),
|
exclude[InheritedNewAbstractMethodProblem]("sbt.ScopedTaskable.canEqual"),
|
||||||
|
// widened ScopedTaskable parameter to (new) supertype Taskable
|
||||||
|
exclude[IncompatibleSignatureProblem]("sbt.Scoped#RichTaskable*.this"),
|
||||||
|
exclude[IncompatibleSignatureProblem]("sbt.TupleSyntax.t*ToTable*"),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.configure(
|
.configure(
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ import Util._
|
||||||
import sbt.util.Show
|
import sbt.util.Show
|
||||||
|
|
||||||
/** A concrete settings system that uses `sbt.Scope` for the scope type. */
|
/** A concrete settings system that uses `sbt.Scope` for the scope type. */
|
||||||
object Def extends Init[Scope] with TaskMacroExtra {
|
object Def extends Init[Scope] with TaskMacroExtra with InitializeImplicits {
|
||||||
type Classpath = Seq[Attributed[File]]
|
type Classpath = Seq[Attributed[File]]
|
||||||
|
|
||||||
def settings(ss: SettingsDefinition*): Seq[Setting[_]] = ss.flatMap(_.settings)
|
def settings(ss: SettingsDefinition*): Seq[Setting[_]] = ss.flatMap(_.settings)
|
||||||
|
|
@ -268,6 +268,14 @@ object Def extends Init[Scope] with TaskMacroExtra {
|
||||||
def taskKey[T](description: String): TaskKey[T] = macro std.KeyMacro.taskKeyImpl[T]
|
def taskKey[T](description: String): TaskKey[T] = macro std.KeyMacro.taskKeyImpl[T]
|
||||||
def inputKey[T](description: String): InputKey[T] = macro std.KeyMacro.inputKeyImpl[T]
|
def inputKey[T](description: String): InputKey[T] = macro std.KeyMacro.inputKeyImpl[T]
|
||||||
|
|
||||||
|
class InitOps[T](private val x: Initialize[T]) extends AnyVal {
|
||||||
|
def toTaskable: Taskable[T] = x
|
||||||
|
}
|
||||||
|
|
||||||
|
class InitTaskOps[T](private val x: Initialize[Task[T]]) extends AnyVal {
|
||||||
|
def toTaskable: Taskable[T] = x
|
||||||
|
}
|
||||||
|
|
||||||
private[sbt] def dummy[T: Manifest](name: String, description: String): (TaskKey[T], Task[T]) =
|
private[sbt] def dummy[T: Manifest](name: String, description: String): (TaskKey[T], Task[T]) =
|
||||||
(TaskKey[T](name, description, DTask), dummyTask(name))
|
(TaskKey[T](name, description, DTask), dummyTask(name))
|
||||||
|
|
||||||
|
|
@ -310,3 +318,12 @@ trait TaskMacroExtra {
|
||||||
@deprecated("unused", "") in: State => Parser[T]
|
@deprecated("unused", "") in: State => Parser[T]
|
||||||
): std.ParserInput[T] = ???
|
): std.ParserInput[T] = ???
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sealed trait InitializeImplicits0 { self: Def.type =>
|
||||||
|
implicit def initOps[T](x: Def.Initialize[T]): Def.InitOps[T] = new Def.InitOps(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed trait InitializeImplicits extends InitializeImplicits0 { self: Def.type =>
|
||||||
|
implicit def initTaskOps[T](x: Def.Initialize[Task[T]]): Def.InitTaskOps[T] =
|
||||||
|
new Def.InitTaskOps(x)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,11 +30,24 @@ sealed trait Scoped extends Equals {
|
||||||
override def hashCode() = (scope, key).##
|
override def hashCode() = (scope, key).##
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A common type for SettingKey and TaskKey so that both can be used as inputs to tasks.*/
|
/** A SettingKey, TaskKey or `Initialize[Task]` that can be converted into an `Initialize[Task]`. */
|
||||||
sealed trait ScopedTaskable[T] extends Scoped {
|
sealed trait Taskable[T] {
|
||||||
def toTask: Initialize[Task[T]]
|
def toTask: Initialize[Task[T]]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sealed trait TaskableImplicits { self: Taskable.type =>
|
||||||
|
implicit def fromInit[T](x: Initialize[T]): Taskable[T] =
|
||||||
|
new Taskable[T] { def toTask = Def.toITask(x) }
|
||||||
|
}
|
||||||
|
|
||||||
|
object Taskable extends TaskableImplicits {
|
||||||
|
implicit def fromITask[T](x: Initialize[Task[T]]): Taskable[T] =
|
||||||
|
new Taskable[T] { def toTask = x }
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A common type for SettingKey and TaskKey so that both can be used as inputs to tasks.*/
|
||||||
|
sealed trait ScopedTaskable[T] extends Scoped with Taskable[T]
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Identifies a setting. It consists of three parts: the scope, the name, and the type of a value associated with this key.
|
* Identifies a setting. It consists of three parts: the scope, the name, and the type of a value associated with this key.
|
||||||
* The scope is represented by a value of type Scope.
|
* The scope is represented by a value of type Scope.
|
||||||
|
|
@ -457,7 +470,7 @@ object Scoped {
|
||||||
Initialize.joinAny[Task](keys).apply(deps => nop.dependsOn(deps: _*))
|
Initialize.joinAny[Task](keys).apply(deps => nop.dependsOn(deps: _*))
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed abstract class RichTaskables[K[L[x]]](final val keys: K[ScopedTaskable])(
|
sealed abstract class RichTaskables[K[L[x]]](final val keys: K[Taskable])(
|
||||||
implicit a: AList[K]
|
implicit a: AList[K]
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
|
@ -469,7 +482,7 @@ object Scoped {
|
||||||
/** Convert the higher-kinded function to a Function1. For tuples that means call `.tupled`. */
|
/** Convert the higher-kinded function to a Function1. For tuples that means call `.tupled`. */
|
||||||
protected def convert[M[_], Ret](f: Fun[M, Ret]): K[M] => Ret
|
protected def convert[M[_], Ret](f: Fun[M, Ret]): K[M] => Ret
|
||||||
|
|
||||||
private[this] val inputs: K[App] = a.transform(keys, λ[ScopedTaskable ~> App](_.toTask))
|
private[this] val inputs: K[App] = a.transform(keys, λ[Taskable ~> App](_.toTask))
|
||||||
|
|
||||||
private[this] def onTasks[T](f: K[Task] => Task[T]): App[T] =
|
private[this] def onTasks[T](f: K[Task] => Task[T]): App[T] =
|
||||||
Def.app[AList.SplitK[K, Task]#l, Task[T]](inputs)(f)(AList.asplit[K, Task](a))
|
Def.app[AList.SplitK[K, Task]#l, Task[T]](inputs)(f)(AList.asplit[K, Task](a))
|
||||||
|
|
@ -484,7 +497,7 @@ object Scoped {
|
||||||
|
|
||||||
// format: off
|
// format: off
|
||||||
|
|
||||||
type ST[X] = ScopedTaskable[X]
|
type ST[X] = Taskable[X]
|
||||||
final class RichTaskable2[A, B](t2: (ST[A], ST[B])) extends RichTaskables[AList.T2K[A, B]#l](t2)(AList.tuple2[A, B]) {
|
final class RichTaskable2[A, B](t2: (ST[A], ST[B])) extends RichTaskables[AList.T2K[A, B]#l](t2)(AList.tuple2[A, B]) {
|
||||||
type Fun[M[_], Ret] = (M[A], M[B]) => Ret
|
type Fun[M[_], Ret] = (M[A], M[B]) => Ret
|
||||||
def identityMap = map(mkTuple2)
|
def identityMap = map(mkTuple2)
|
||||||
|
|
@ -608,16 +621,17 @@ trait TupleSyntax {
|
||||||
// format: off
|
// format: off
|
||||||
|
|
||||||
// this is the least painful arrangement I came up with
|
// this is the least painful arrangement I came up with
|
||||||
implicit def t2ToTable2[A, B](t2: (ScopedTaskable[A], ScopedTaskable[B])): RichTaskable2[A, B] = new RichTaskable2(t2)
|
type ST[T] = Taskable[T]
|
||||||
implicit def t3ToTable3[A, B, C](t3: (ScopedTaskable[A], ScopedTaskable[B], ScopedTaskable[C])): RichTaskable3[A, B, C] = new RichTaskable3(t3)
|
implicit def t2ToTable2[A, B](t2: (ST[A], ST[B])): RichTaskable2[A, B] = new RichTaskable2(t2)
|
||||||
implicit def t4ToTable4[A, B, C, D](t4: (ScopedTaskable[A], ScopedTaskable[B], ScopedTaskable[C], ScopedTaskable[D])): RichTaskable4[A, B, C, D] = new RichTaskable4(t4)
|
implicit def t3ToTable3[A, B, C](t3: (ST[A], ST[B], ST[C])): RichTaskable3[A, B, C] = new RichTaskable3(t3)
|
||||||
implicit def t5ToTable5[A, B, C, D, E](t5: (ScopedTaskable[A], ScopedTaskable[B], ScopedTaskable[C], ScopedTaskable[D], ScopedTaskable[E])): RichTaskable5[A, B, C, D, E] = new RichTaskable5(t5)
|
implicit def t4ToTable4[A, B, C, D](t4: (ST[A], ST[B], ST[C], ST[D])): RichTaskable4[A, B, C, D] = new RichTaskable4(t4)
|
||||||
implicit def t6ToTable6[A, B, C, D, E, F](t6: (ScopedTaskable[A], ScopedTaskable[B], ScopedTaskable[C], ScopedTaskable[D], ScopedTaskable[E], ScopedTaskable[F])): RichTaskable6[A, B, C, D, E, F] = new RichTaskable6(t6)
|
implicit def t5ToTable5[A, B, C, D, E](t5: (ST[A], ST[B], ST[C], ST[D], ST[E])): RichTaskable5[A, B, C, D, E] = new RichTaskable5(t5)
|
||||||
implicit def t7ToTable7[A, B, C, D, E, F, G](t7: (ScopedTaskable[A], ScopedTaskable[B], ScopedTaskable[C], ScopedTaskable[D], ScopedTaskable[E], ScopedTaskable[F], ScopedTaskable[G])): RichTaskable7[A, B, C, D, E, F, G] = new RichTaskable7(t7)
|
implicit def t6ToTable6[A, B, C, D, E, F](t6: (ST[A], ST[B], ST[C], ST[D], ST[E], ST[F])): RichTaskable6[A, B, C, D, E, F] = new RichTaskable6(t6)
|
||||||
implicit def t8ToTable8[A, B, C, D, E, F, G, H](t8: (ScopedTaskable[A], ScopedTaskable[B], ScopedTaskable[C], ScopedTaskable[D], ScopedTaskable[E], ScopedTaskable[F], ScopedTaskable[G], ScopedTaskable[H])): RichTaskable8[A, B, C, D, E, F, G, H] = new RichTaskable8(t8)
|
implicit def t7ToTable7[A, B, C, D, E, F, G](t7: (ST[A], ST[B], ST[C], ST[D], ST[E], ST[F], ST[G])): RichTaskable7[A, B, C, D, E, F, G] = new RichTaskable7(t7)
|
||||||
implicit def t9ToTable9[A, B, C, D, E, F, G, H, I](t9: (ScopedTaskable[A], ScopedTaskable[B], ScopedTaskable[C], ScopedTaskable[D], ScopedTaskable[E], ScopedTaskable[F], ScopedTaskable[G], ScopedTaskable[H], ScopedTaskable[I])): RichTaskable9[A, B, C, D, E, F, G, H, I] = new RichTaskable9(t9)
|
implicit def t8ToTable8[A, B, C, D, E, F, G, H](t8: (ST[A], ST[B], ST[C], ST[D], ST[E], ST[F], ST[G], ST[H])): RichTaskable8[A, B, C, D, E, F, G, H] = new RichTaskable8(t8)
|
||||||
implicit def t10ToTable10[A, B, C, D, E, F, G, H, I, J](t10: (ScopedTaskable[A], ScopedTaskable[B], ScopedTaskable[C], ScopedTaskable[D], ScopedTaskable[E], ScopedTaskable[F], ScopedTaskable[G], ScopedTaskable[H], ScopedTaskable[I], ScopedTaskable[J])): RichTaskable10[A, B, C, D, E, F, G, H, I, J] = new RichTaskable10(t10)
|
implicit def t9ToTable9[A, B, C, D, E, F, G, H, I](t9: (ST[A], ST[B], ST[C], ST[D], ST[E], ST[F], ST[G], ST[H], ST[I])): RichTaskable9[A, B, C, D, E, F, G, H, I] = new RichTaskable9(t9)
|
||||||
implicit def t11ToTable11[A, B, C, D, E, F, G, H, I, J, K](t11: (ScopedTaskable[A], ScopedTaskable[B], ScopedTaskable[C], ScopedTaskable[D], ScopedTaskable[E], ScopedTaskable[F], ScopedTaskable[G], ScopedTaskable[H], ScopedTaskable[I], ScopedTaskable[J], ScopedTaskable[K])): RichTaskable11[A, B, C, D, E, F, G, H, I, J, K] = new RichTaskable11(t11)
|
implicit def t10ToTable10[A, B, C, D, E, F, G, H, I, J](t10: (ST[A], ST[B], ST[C], ST[D], ST[E], ST[F], ST[G], ST[H], ST[I], ST[J])): RichTaskable10[A, B, C, D, E, F, G, H, I, J] = new RichTaskable10(t10)
|
||||||
|
implicit def t11ToTable11[A, B, C, D, E, F, G, H, I, J, K](t11: (ST[A], ST[B], ST[C], ST[D], ST[E], ST[F], ST[G], ST[H], ST[I], ST[J], ST[K])): RichTaskable11[A, B, C, D, E, F, G, H, I, J, K] = new RichTaskable11(t11)
|
||||||
|
|
||||||
implicit def t2ToApp2[A, B](t2: (Initialize[A], Initialize[B])): Apply2[A, B] = new Apply2(t2)
|
implicit def t2ToApp2[A, B](t2: (Initialize[A], Initialize[B])): Apply2[A, B] = new Apply2(t2)
|
||||||
implicit def t3ToApp3[A, B, C](t3: (Initialize[A], Initialize[B], Initialize[C])): Apply3[A, B, C] = new Apply3(t3)
|
implicit def t3ToApp3[A, B, C](t3: (Initialize[A], Initialize[B], Initialize[C])): Apply3[A, B, C] = new Apply3(t3)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
/*
|
||||||
|
* sbt
|
||||||
|
* Copyright 2011 - 2018, Lightbend, Inc.
|
||||||
|
* Copyright 2008 - 2010, Mark Harrah
|
||||||
|
* Licensed under Apache License 2.0 (see LICENSE)
|
||||||
|
*/
|
||||||
|
|
||||||
|
package sbt.test
|
||||||
|
|
||||||
|
import sbt._
|
||||||
|
import sbt.Def.Initialize
|
||||||
|
import sbt.TupleSyntax._
|
||||||
|
|
||||||
|
object TupleSyntaxTest {
|
||||||
|
def t1[T](a: SettingKey[T], b: TaskKey[T], c: Initialize[T], d: Initialize[Task[T]]) = {
|
||||||
|
(a, b, c.toTaskable, d.toTaskable).map((x: T, y: T, z: T, w: T) => "" + x + y + z + w)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1327,10 +1327,12 @@ object Defaults extends BuildCommon {
|
||||||
case _ => None
|
case _ => None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def resourceMappings = relativeMappings(unmanagedResources, unmanagedResourceDirectories)
|
def resourceMappings = relativeMappings(unmanagedResources, unmanagedResourceDirectories)
|
||||||
|
|
||||||
def relativeMappings(
|
def relativeMappings(
|
||||||
files: ScopedTaskable[Seq[File]],
|
files: Taskable[Seq[File]],
|
||||||
dirs: ScopedTaskable[Seq[File]]
|
dirs: Taskable[Seq[File]]
|
||||||
): Initialize[Task[Seq[(File, String)]]] =
|
): Initialize[Task[Seq[(File, String)]]] =
|
||||||
Def.task {
|
Def.task {
|
||||||
val rdirs = dirs.toTask.value.toSet
|
val rdirs = dirs.toTask.value.toSet
|
||||||
|
|
@ -1340,14 +1342,27 @@ object Defaults extends BuildCommon {
|
||||||
case _ => None
|
case _ => None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def collectFiles(
|
def collectFiles(
|
||||||
dirs: ScopedTaskable[Seq[File]],
|
dirs: Taskable[Seq[File]],
|
||||||
filter: ScopedTaskable[FileFilter],
|
filter: Taskable[FileFilter],
|
||||||
excludes: ScopedTaskable[FileFilter]
|
excludes: Taskable[FileFilter]
|
||||||
): Initialize[Task[Seq[File]]] =
|
): Initialize[Task[Seq[File]]] =
|
||||||
Def.task {
|
Def.task {
|
||||||
dirs.toTask.value.descendantsExcept(filter.toTask.value, excludes.toTask.value).get
|
dirs.toTask.value.descendantsExcept(filter.toTask.value, excludes.toTask.value).get
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def relativeMappings( // forward to widened variant
|
||||||
|
files: ScopedTaskable[Seq[File]],
|
||||||
|
dirs: ScopedTaskable[Seq[File]]
|
||||||
|
): Initialize[Task[Seq[(File, String)]]] = relativeMappings(files: Taskable[Seq[File]], dirs)
|
||||||
|
|
||||||
|
def collectFiles( // forward to widened variant
|
||||||
|
dirs: ScopedTaskable[Seq[File]],
|
||||||
|
filter: ScopedTaskable[FileFilter],
|
||||||
|
excludes: ScopedTaskable[FileFilter]
|
||||||
|
): Initialize[Task[Seq[File]]] = collectFiles(dirs: Taskable[Seq[File]], filter, excludes)
|
||||||
|
|
||||||
def artifactPathSetting(art: SettingKey[Artifact]): Initialize[File] =
|
def artifactPathSetting(art: SettingKey[Artifact]): Initialize[File] =
|
||||||
Def.setting {
|
Def.setting {
|
||||||
val f = artifactName.value
|
val f = artifactName.value
|
||||||
|
|
@ -2021,20 +2036,30 @@ object Defaults extends BuildCommon {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object Classpaths {
|
object Classpaths {
|
||||||
import Defaults._
|
import Defaults._
|
||||||
import Keys._
|
import Keys._
|
||||||
|
|
||||||
def concatDistinct[T](
|
def concatDistinct[T](a: Taskable[Seq[T]], b: Taskable[Seq[T]]): Initialize[Task[Seq[T]]] =
|
||||||
|
Def.task((a.toTask.value ++ b.toTask.value).distinct)
|
||||||
|
|
||||||
|
def concat[T](a: Taskable[Seq[T]], b: Taskable[Seq[T]]): Initialize[Task[Seq[T]]] =
|
||||||
|
Def.task(a.toTask.value ++ b.toTask.value)
|
||||||
|
|
||||||
|
def concatSettings[T](a: Initialize[Seq[T]], b: Initialize[Seq[T]]): Initialize[Seq[T]] =
|
||||||
|
Def.setting { a.value ++ b.value }
|
||||||
|
|
||||||
|
def concatDistinct[T]( // forward to widened variant
|
||||||
a: ScopedTaskable[Seq[T]],
|
a: ScopedTaskable[Seq[T]],
|
||||||
b: ScopedTaskable[Seq[T]]
|
b: ScopedTaskable[Seq[T]]
|
||||||
): Initialize[Task[Seq[T]]] = Def.task {
|
): Initialize[Task[Seq[T]]] = concatDistinct(a: Taskable[Seq[T]], b)
|
||||||
(a.toTask.value ++ b.toTask.value).distinct
|
|
||||||
}
|
|
||||||
def concat[T](a: ScopedTaskable[Seq[T]], b: ScopedTaskable[Seq[T]]): Initialize[Task[Seq[T]]] =
|
def concat[T](a: ScopedTaskable[Seq[T]], b: ScopedTaskable[Seq[T]]): Initialize[Task[Seq[T]]] =
|
||||||
Def.task { a.toTask.value ++ b.toTask.value }
|
concat(a: Taskable[Seq[T]], b) // forward to widened variant
|
||||||
|
|
||||||
def concatSettings[T](a: SettingKey[Seq[T]], b: SettingKey[Seq[T]]): Initialize[Seq[T]] =
|
def concatSettings[T](a: SettingKey[Seq[T]], b: SettingKey[Seq[T]]): Initialize[Seq[T]] =
|
||||||
Def.setting { a.value ++ b.value }
|
concatSettings(a: Initialize[Seq[T]], b) // forward to widened variant
|
||||||
|
|
||||||
lazy val configSettings: Seq[Setting[_]] = classpaths ++ Seq(
|
lazy val configSettings: Seq[Setting[_]] = classpaths ++ Seq(
|
||||||
products := makeProducts.value,
|
products := makeProducts.value,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
* sbt
|
||||||
|
* Copyright 2011 - 2018, Lightbend, Inc.
|
||||||
|
* Copyright 2008 - 2010, Mark Harrah
|
||||||
|
* Licensed under Apache License 2.0 (see LICENSE)
|
||||||
|
*/
|
||||||
|
|
||||||
|
package sbt.test
|
||||||
|
|
||||||
|
import sbt._
|
||||||
|
import sbt.Classpaths._
|
||||||
|
import sbt.Def.Initialize
|
||||||
|
|
||||||
|
class ClasspathsTest[T](
|
||||||
|
settKey: SettingKey[Seq[T]],
|
||||||
|
taskKey: TaskKey[Seq[T]],
|
||||||
|
initVal: Initialize[Seq[T]],
|
||||||
|
taskVal: Initialize[Task[Seq[T]]],
|
||||||
|
) {
|
||||||
|
|
||||||
|
def testConcat() = {
|
||||||
|
concat(settKey, settKey)
|
||||||
|
concat(settKey, taskKey)
|
||||||
|
concat(settKey, initVal)
|
||||||
|
concat(settKey, taskVal)
|
||||||
|
concat(taskKey, settKey)
|
||||||
|
concat(taskKey, taskKey)
|
||||||
|
concat(taskKey, initVal)
|
||||||
|
concat(taskKey, taskVal)
|
||||||
|
concat(initVal, settKey)
|
||||||
|
concat(initVal, taskKey)
|
||||||
|
concat(initVal, initVal)
|
||||||
|
concat(initVal, taskVal)
|
||||||
|
concat(taskVal, settKey)
|
||||||
|
concat(taskVal, taskKey)
|
||||||
|
concat(taskVal, initVal)
|
||||||
|
concat(taskVal, taskVal)
|
||||||
|
}
|
||||||
|
|
||||||
|
def testConcatSettings() = {
|
||||||
|
concatSettings(settKey, settKey)
|
||||||
|
concatSettings(settKey, initVal)
|
||||||
|
concatSettings(initVal, settKey)
|
||||||
|
concatSettings(initVal, initVal)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue