Создание мультиплатформенных проектов с помощью Gradle

Мультиплатформенные проекты являются экспериментальной особенностью в Kotlin 1.2 и 1.3. Все языки и инструменты, описанные в этом документе, могут быть изменены в будущих версиях Kotlin.

В этом документе описывается, как мультиплатформенные проекты Kotlin создаются и настраиваются с использованием Gradle. Могут использоваться только версии Gradle 4.7 и выше, более ранние версии Gradle не поддерживаются.

Поддержка Gradle Kotlin DSL еще не реализована для мультиплатформенных проектов, но она будет добавлена в будущие обновления. Пожалуйста, используйте Groovy DSL в сценариях сборки.

Настройка мультиплатформенного проекта

Можно создать новый мультиплатформенный проект в IDE, выбрав один из шаблонов мультиплатформенных проектов в диалоговом окне «Новый проект» в разделе «Kotlin».

Например, если выбран «Kotlin (Multiplatform Library)», то создается проект библиотеки, ориентированный на 3 цели: JVM, JS и используемую Native платформу. Они сконфигурированы в сценарии build.gradle следующим образом:

plugins {
    id 'org.jetbrains.kotlin.multiplatform' version '{{ site.data.releases.latest.version }}'
}

repositories {
    mavenCentral()
}

kotlin {
    targets {
        fromPreset(presets.jvm, 'jvm')
        fromPreset(presets.js, 'js')
        fromPreset(presets.mingwX64, 'mingw')
    }
    
    sourceSets { /* ... */ }
}

Три цели создаются из предустановок, которые предоставляют некоторую конфигурацию по умолчанию. Здесь приведены предустановки для каждой поддерживаемой платформы.

Затем устанавливаются наборы исходных кодов и их зависимости:

plugins { /* ... */ }

kotlin {
    targets { /* ... */ }

    sourceSets { 
        commonMain {
            dependencies {
                implementation 'org.jetbrains.kotlin:kotlin-stdlib-common'
            }
        }
        commonTest {
            dependencies {
                implementation 'org.jetbrains.kotlin:kotlin-test-common'
                implementation 'org.jetbrains.kotlin:kotlin-test-annotations-common'
            }
        }
        jvmMain {
            dependencies {
                implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'
            }
        }
        jvmTest {
            dependencies {
                implementation 'org.jetbrains.kotlin:kotlin-test'
                implementation 'org.jetbrains.kotlin:kotlin-test-junit'
            }
        }
        jsMain { /* ... */ }
        jsTest { /* ... */ }
        mingwMain { /* ... */ }
        mingwTest { /* ... */ }
    }
}

Представленные по умолчанию имена наборов исходных кодов предназначены для основных и тестовых исходных кодов, указанных выше. Наборы commonMain и commonTest включаются в основную и тестовую компиляции, соответственно, для всех целевых платформ. Обратите внимание на то, что зависимости для общих наборов исходных кодов commonMain и commonTest общие, а библиотеки платформ идут для конкретных платформ.

Подробные сведения о структуре проекта и DSL можно найти в следующих разделах.

Плагин Gradle

Для создания мультиплатформенного проекта с нуля применим к проекту плагин kotlin-multiplatform путем добавления следующего кода в build.gradle

plugins {
    id 'org.jetbrains.kotlin.multiplatform' version '{{ site.data.releases.latest.version }}'
}

Так создается расширение kotlin на верхнем уровне. Можно получить к нему доступ в скрипте сборки для:

Настройка целей

Цель - часть сборки, отвечающей за компиляцию, тестирование и упаковку части программного обеспечения, предназначенного для одной из поддерживаемых платформ.

Поскольку платформы различны, цели также построены по-разному и имеют разные настройки для конкретной платформы. Плагин Gradle объединяет ряд предустановок для поддерживаемых платформ. Предустановку можно использовать для создания цели, просто указывая имя, следующим образом:

kotlin {
    targets {
        fromPreset(presets.jvm, 'jvm6') // Create a JVM target by the name 'jvm6'
        
        fromPreset(presets.linuxX64, 'linux') { 
            /* You can specify additional settings for the 'linux' target here */
        } 
    }
}

Создание цели требует компиляции Kotlin один или несколько раз. Каждая компиляция цели Kotlin может служить другой цели (например, производственный код, тесты) и включать различные наборы исходных кодов. К компиляциям цели можно обратиться в DSL, например, чтобы получить имена задач, файлы зависимостей и выходы компиляции:

kotlin {
    targets {
        fromPreset(presets.jvm, 'jvm6') { 
            def mainKotlinTaskName = compilations.main.compileKotlinTaskName
            def mainOutputs = compilations.main.output
            def testRuntimeClasspath = compilations.test.runtimeDependencyFiles            
        }
    }
}

Для изменения параметров компиляции компилятора Kotlin следует использовать задачу компиляции, которая может быть найдена по ее имени:

kotlin {
    targets {
        fromPreset(presets.jvm, 'jvm8') {
            // Configure a single target's compilations (main and test)
            compilations.all {
                tasks[compileKotlinTaskName].kotlinOptions { 
                    jvmTarget = '1.8'
                }
            }
        }
        
        /* ... */
        
        // Configure all compilations of all targets:
        all { 
            compilations.all {
                tasks[compileKotlinTaskName].kotlinOptions {
                    allWarningsAsErrors = true
                }
            }
        }
    }
}

Все цели могут совместно использовать некоторые исходные коды и также могут иметь специфические для каждой платформы исходные коды в своих компиляциях. Подробнее см. Настройка наборов исходных кодов.

Для некоторых целей может потребоваться дополнительная настройка. Примеры для Android и iOS см. в руководстве Мультиплатформенный проект: iOS и Android.

Поддерживаемые платформы

Существуют предустановки целей, которые можно применять fromPreset(presets.<имяПредустановки>, '<имяЦели>'), как описано выше, для следующих целевых платформ:

  • jvm для Kotlin/JVM. Примечание. цели jvm не скомпилируют Java;

  • js для Kotlin/JS;

  • android для приложений и библиотек Android. Обратите внимание на то, что также должен применяться один из плагинов Android Gradle;

  • Целевые предустановки Kotlin/Native (см. Примечания ниже):

    • androidNativeArm32 и androidNativeArm64 для Android NDK;
    • iosArm32, iosArm64, iosX64 для iOS;
    • linuxArm32Hfp, linuxMips32, linuxMipsel32, linuxX64 для Linux
    • macosX64 для MacOS
    • mingwX64 для Windows

    Обратите внимание на то, что некоторые из целей Kotlin / Native требуют создания соответствующей хост-машины.

Конфигурирование наборов исходных кодов

Набор исходных кодов Kotlin - это коллекция исходных кодов Kotlin, а также их ресурсы, зависимости и языковые настройки, которые могут принимать участие в компиляции одной или нескольких целей Kotlin.

Если применяются целевые предустановки, то некоторые наборы исходных кодов создаются и настраиваются по умолчанию. См. Макет проекта по умолчанию.

Наборы источников настраиваются внутри блока sourceSets { ... } расширения kotlin { ... }:

kotlin { 
    targets { /* ... */ }
    
    sourceSets { 
        foo { /* ... */ } // create or configure a source set by the name 'foo' 
        bar { /* ... */ }
    }
}

Примечание: создание набора исходных кодов не связывает его с какой-либо целью. Некоторые наборы исходных кодов предопределены и, таким образом, уже скомпилированы по умолчанию. Однако настраиваемые наборы исходных кодов всегда должны быть явно направлены на компиляцию. См. Подключение наборов исходных кодов.

Имена наборов исходных кодов чувствительны к регистру. При обращении к набору исходных кодов по умолчанию убедитесь, что префикс имени совпадает с именем цели, например, источником, заданным iosX64Main для цели iosX64.

Набор исходных кодов сам по себе является платформенно-независимым, но его можно рассматривать в качестве специфичного для платформы, если он скомпилирован только для одной этой платформы. Поэтому исходный набор может содержать либо общий код, разделяемый между платформами, либо код, специфичный для платформы.

Чтобы добавить исходные каталоги и ресурсы Kotlin в исходный набор, используйте его SourceDirectorySetы kotlin и resources:

kotlin { 
    sourceSets { 
        commonMain {
            kotlin.srcDir('src')
            resources.srcDir('res')
        } 
    }
}

Подключение наборов исходных кодов

Наборы исходных кодов Kotlin могут быть связаны с отношением ‘зависит от’, поэтому, если набор исходных кодов ‘foo’ зависит от набора исходных кодов ‘bar’, тогда:

  • всякий раз, когда foo компилируется для определенной цели, bar также участвует в этой компиляции и также скомпилируется в одну и ту же целевую бинарную форму, такую как файлы классов JVM или JS-код;
  • ресурсы bar всегда обрабатываются и копируются вместе с ресурсами foo;
  • исходный код foo «видит» объявления bar, в том числе и внутренние, а также зависимости от bar, даже те, которые указаны в качестве зависимостей implementation;
  • foo может содержать реализации, специфичные для платформы, для ожидаемых объявлений bar;

Рекурсивные зависимости в наборах исходных кодов запрещены.

DSL наборов исходных кодов может использоваться для определения соединений между наборами исходных кодов:

 kotlin { 
     sourceSets { 
         commonMain { /* ... */ }
         allJvm {
             dependsOn commonMain
             /* ... */
         } 
     }
 }

Пользовательские наборы исходных кодов, созданные в дополнение к стандартным, должны быть явно включены в иерархию зависимостей для того, чтобы иметь возможность использовать объявления из других наборов исходных кодов и, самое главное, принимать участие в компиляциях. Чаще всего им нужен оператор dependsOn commonMain или dependsOn commonTest, а некоторые из наборов исходных кодов для конкретной платформы должны напрямую или косвенно зависеть от пользовательских:

kotlin { 
    targets {
        fromPreset(presets.mingwX64, 'windows')
        fromPreset(presets.linuxX64, 'linux')
        /* ... */
    }
    sourceSets {
        desktopTest { // custom source set with tests for the two targets
            dependsOn commonTest
            /* ... */
        }
        windowsTest { // default test source set for target 'windows'
            dependsOn desktopTest
            /* ... */
        }
        linuxTest { // default test source set for target 'linux'
            dependsOn desktopTest
        }
        /* ... */
    }
}

Добавление зависимостей

Для добавления зависимости к набору исходных кодов используем блок dependencies { ... } наборов исходных кодов DSL. Поддерживаются четыре вида зависимостей:

  • Зависимости api используются как во время компиляции, так и во время выполнения, и экспортируются в библиотеки. Если в публичном API текущего модуля используются какие-либо типы из зависимости, то это должна быть зависимость api;
  • Зависимости implementation используются во время компиляции и во время выполнения текущего модуля, но не отображаются при компиляции других модулей в зависимости от того, какая зависимость implementation используется. Тип зависимости implementation должен использоваться для зависимостей, необходимых для внутренней логики модуля. Если модуль является конечным приложением, которое не публикуется, оно может использовать зависимости implementation вместо api.
  • Зависимости compileOnly используются только для компиляции текущего модуля и недоступны ни во время выполнения, ни при компиляции других модулей. Эти зависимости должны использоваться для API, имеющих стороннюю реализацию, доступную во время выполнения.
  • Зависимости runtimeOnly доступны во время выполнения, но не отображаются во время компиляции никаких модулей.

Зависимости для каждого набора исходных кодов указаны следующим образом:

kotlin {
    sourceSets {
        commonMain {
            dependencies {
                api 'com.example:foo-metadata:1.0'
            }
        }
        jvm6Main {
            dependencies {
                api 'com.example:foo-jvm6:1.0'
            }
        }
    }
}

Обратите внимание на то, что для того, чтобы IDE правильно анализировала зависимости общих исходных кодов, общие наборы исходных кодов должны иметь соответствующие зависимости от пакетов метаданных Kotlin в дополнение к зависимостям артефакта для конкретной платформы от наборов исходных кодов для этой же платформы. Обычно при использовании опубликованной библиотеки требуется артефакт с суффиксом -common (как в случае kotlin-stdlib-common) или -metadata (если только он не опубликован с метаданными Gradle, как описано ниже).

Однако зависимость project('...') от другого мультиплатформенного проекта автоматически настраивается для соответствующей цели. Достаточно указать одну зависимость project('...') в зависимостях набора исходных кодов, а компиляции, включающие в себя исходный набор, получат соответствующий артефакт для конкретной платформы для этого проекта, учитывая, что он имеет совместимую цель.

Аналогично, если многоплатформенная библиотека публикуется в экспериментальном режиме публикации метаданных Gradle и проект настроен так, чтобы потреблять метаданные, то достаточно указать зависимость только один раз для общего набора исходных кодов. В противном случае каждый набор ресурсов для конкретной платформы должен быть снабжен соответствующим платформенным модулем библиотеки в дополнение к общему модулю, как показано выше.

Альтернативным способом указания зависимостей является использование встроенного DSL Gradle на верхнем уровне с именами конфигурации, следующими за шаблоном <sourceSetName><DependencyKind>:

dependencies {
    commonMainApi 'com.example:foo-common:1.0'
    jvm6MainApi 'com.example:foo-jvm6:1.0'
}

Настройки языка

Можно настроить языковые настройки для всех наборов источников одновременно:

kotlin.sourceSets.all {
    languageSettings {
        progressiveMode = true
    }
}

Языковые настройки набора источников влияют на то, как исходные коды анализируются в IDE. Из-за существующих ограничений в сборке Gradle используются только настройки языка исходного набора компиляции.

Настройки языка проверяются на согласованность между наборами исходных кодов в зависимости друг от друга. А именно, если foo зависит от bar:

  • foo должен установить значение languageVersion, большее или равное значению bar;
  • foo должен включить все нестабильные языковые функции, которые позволяет bar (к функциям исправления нет такого требования);
  • foo следует использовать все экспериментальные аннотации, которые использует bar;
  • apiVersion, возможности исправления языка и progressiveMode могут быть установлены произвольно;

Макет проекта по умолчанию

По умолчанию каждый проект содержит два набора источников commonMain и commonTest, где можно разместить весь код, который должен быть общим для всех целевых платформ. Эти наборы исходных кодов добавляются, соответственно, к каждой сборке и тестовой компиляции.

Затем, после добавления цели, для него создаются компиляции по умолчанию:

  • main и test компиляции для JVM, JS и Native;
  • компиляция на Android;

Для каждой компиляции есть набор исходных кодов по умолчанию, заданный под именем, составленным как <имяЦели><имяКомпиляции>. Этот набор исходных кодов по умолчанию участвует в компиляции, и поэтому он должен использоваться для кода и зависимостей для платформы, а также для добавления других источников в компиляцию с помощью параметра ‘depends on’. Например, проект с целями jvm6 (JVM) и nodeJs (JS) будут иметь наборы исходных кодов: commonMain, commonTest, jvm6Main, jvm6Test, nodeJsMain, nodeJsTest.

Многочисленные варианты использования охватываются только стандартными наборами исходных кодов и не требуют настраиваемых наборов исходных кодов.

Каждый установленный по умолчанию набор исходных кодов имеет свои исходные коды Kotlin в директории src/<sourceSetName>/kotlin и ресурсы под src/<sourceSetName>/resources.

В проектах Android для каждого набора файлов Android создаются дополнительные исходные коды Kotlin. Если у цели Android есть имя foo, то исходный набор Android bar получает экземпляр набора исходных кодов Kotlin fooBar. Компиляции Котлин, однако, способны потреблять источники Котлин из всех каталогов src/bar/java, src/bar/kotlin и src/fooBar/kotlin. Источники Java считываются только из первого из этих каталогов.

Запуск тестов

Задача теста создается под именем <имяЦели>Test для каждой цели, подходящей для тестирования. Запустим задачу check для запуска тестов для всех целей.

Поскольку набор исходных кодов по умолчанию commonTest по умолчанию добавляется ко всем тестовым компиляциям, там могут быть размещены тесты и инструменты тестирования, которые необходимы на всех целевых платформах.

kotlin.test API доступен для мультиплатформенных тестов. Добавьте зависимости kotlin-test-common и kotlin-test-annotations-common в commonTest для использования DefaultAsserter и аннотаций @Test/@Ignore/@BeforeTest/@AfterTest в общих тестах.

Для JVM используем kotlin-test-junit или kkotlin-test-testng для соответствующего сопоставления исполнителей и составления аннотаций.

Для Kotlin/JS добавим kotlin-test-js в качестве тестовой зависимости. На этом этапе тестовые задачи для Kotlin/JS создаются, но не запускают тесты по умолчанию; они должны быть настроены вручную для запуска тестов с помощью тестового фреймворка JavaScript.

Цели Kotlin/Native не требуют дополнительных тестовых зависимостей, а реализации API kotlin.test уже встроены.

Публикация многоплатформенной библиотеки

Набор целевых платформ определяется автором мультиплатформенной библиотеки, и они должны предоставлять все реализации конкретной платформы для данной библиотеки. Добавление новых целей для многоплатформенной библиотеки со стороны потребителя не поддерживается.

Библиотека, созданная из многоплатформенного проекта, может быть опубликована в репозитории Maven с плагином Gradle maven-publish, который может применяться следующим образом:

plugins {
    /* ... */
    id 'maven-publish'
}

При применении данного плагина публикации по умолчанию создаются для каждой из целей, которая может быть создана на текущем хосте. Это требует задания в проекте group и version. Идентификаторы артефакта по умолчанию следуют шаблону <имяПроекта>-<имяЦелиСтрочнымиБуквами>, например, sample-lib-nodejs для цели, названной nodeJs в проекте sample-lib.

Кроме того, по умолчанию добавляется дополнительная публикация, содержащая сериализованные объявления Kotlin и используемая IDE для анализа мультиплатформенных библиотек.

По умолчанию в каждую публикацию добавляется исходный код JAR, а также его главный артефакт. Исходные коды JAR содержат исходные коды, используемые при компиляции цели main.

Координаты Maven могут быть изменены и в публикацию внутри блока targets { ... } могут быть добавлены дополнительные файлы артефактов:

kotlin {
    targets {
        fromPreset(presets.jvm, 'jvm6') {
            /* ... */
            mavenPublication { 
                artifactId = 'sample-lib-jvm'
                artifact(jvmJavadocJar)
            }
        }
    }
}

Экспериментальный режим публикации метаданных

Экспериментальный режим публикации и зависимости может быть включен путем добавления enableFeaturePreview('GRADLE_METADATA') в файл settings.gradle. При включении метаданных Gradle добавляется дополнительная публикация, которая ссылается на целевые публикации в качестве ее вариантов. Идентификатор артефакта этой публикации соответствует имени проекта.

Публикация метаданных Gradle - это экспериментальная функция Gradle, не гарантирующая обратную совместимость. Будущие версии Gradle могут не разрешить зависимость от библиотеки, опубликованной с текущими версиями метаданных Gradle. Авторы библиотек рекомендуют использовать его для публикации экспериментальных версий библиотеки наряду со стабильным механизмом публикации до тех пор, пока эта функция не будет считаться стабильной.

Если библиотека опубликована с включенными метаданными Gradle, а потребитель также включает метаданные, то потребитель может указать одну зависимость от библиотеки в общем наборе исходных кодов, и будет выбран соответствующий вариант для конкретной платформы для каждой из компиляций. Рассмотрим библиотеку sample-lib, созданную для JVM и JS, и опубликованную с экспериментальными метаданными Gradle. Тогда для потребителей достаточно добавить enableFeaturePreview('GRADLE_METADATA) и указать одну зависимость:

kotlin {
    targets {
        fromPreset(presets.jvm, 'jvm6')
        fromPreset(presets.js, 'nodeJs')
    }
    sourceSets {
        commonMain {
            dependencies {
                api 'com.example:sample-lib:1.0' 
                // is resolved to `sample-lib-jvm` for JVM, `sample-lib-js` for JS 
            }
        }
    }
}

Неоднозначные цели

В многоплатформенной библиотеке возможно иметь более одной цели для одной платформы. Например, эти цели могут предоставлять один и тот же API и различать библиотеки, с которыми они взаимодействуют во время выполнения, например, тестирование фреймворков или логирование.

Однако зависимости от такой многоплатформенной библиотеки могут быть неоднозначными и, таким образом, не могут быть разрешены при определенных условиях:

  • Используется зависимость project('...') от многоплатформенной библиотеки. Заменим его зависимостью project(path: '...', configuration: '...'). Используем соответствующую настройку элементов времени выполнения, например jvm6RuntimeElements. Из-за существующих ограничений эта зависимость должна быть помещена в блок верхнего уровня dependencies { ... }, а не в зависимости от набора исходных кодов.
  • Используется опубликованная библиотечная зависимость. Если библиотека опубликована с экспериментальными метаданными Gradle, все равно можно заменить одну зависимость однозначными зависимостями на отдельные целевые модули, как если бы у нее отсутствовали экспериментальные метаданные Gradle.
  • В обоих вышеописанных случаях другое решение заключается в том, чтобы пометить целевые объекты настраиваемым атрибутом. Это, однако, должно быть сделано как со стороны автора библиотеки, так и для сторон потребителей. Ответственность автора библиотеки заключается в том, чтобы сообщать об этом атрибуту и его значениям потребителям;

Добавим следующий код и к библиотеке, и к потребительским проектам. Потребителю может потребоваться только отметить одну цель с атрибутом:

def testFrameworkAttribute = Attribute.of('com.example.testFramework', String)
      
kotlin {
     targets {
         fromPreset(presets.jvm, 'junit') {
             attributes {
                 attribute(testingFrameworkAttribute, 'junit')
             }
         }
         fromPreset(presets.jvm, 'testng') {
             attributes {
                 attribute(testingFrameworkAttribute, 'testng')
             }
         }
     }
 }

Использование целей Kotlin/Native

Важно отметить, что некоторые из целей Kotlin/Native могут быть созданы только с помощью соответствующей хост-машины:

  • Цели Linux могут быть построены только на хосте Linux;
  • Для целей Windows требуется хост Windows;
  • Цели macOS и iOS могут быть созданы только на хосте macOS;
  • Для мобильных целей Android требуется хост Linux или macOS;

Цель, неподдерживаемая текущим хостом, игнорируется во время сборки и поэтому не публикуется. Автор библиотеки может захотеть настроить сборку и публикацию с разных хостов в соответствии с требованиями целевых платформ библиотеки.

Виды выходных данных Kotlin/Native

По умолчанию цель Kotlin/Native скомпилирована до артефакта библиотеки *.klib, который может быть использован самим Kotlin/Native в качестве зависимости, но не может быть запущен или использован в качестве родной библиотеки.

Для того чтобы привязать бинарный файл как дополнение к библиотеке Kotlin/Native, добавим один или несколько outputKinds, которые могут быть:

  • executable для исполняемой программы;
  • dynamic для динамической библиотеки;
  • static для статической библиотеки;
  • framework для фреймворка Objective-C (поддерживается только для macOS и iOS)

Это можно сделать следующим образом:

kotlin {
    targets {
        fromPreset(presets.linuxX64, 'linux') {
            compilations.main.outputKinds 'executable' // could be 'static', 'dynamic'
        }
    }
}

Это создает дополнительные связанные задачи для бинарных файлов отладки и релиза. Доступ к задачам можно получить после оценки проекта из компиляции, например, getLinkTask('executable', 'release') или getLinkTaskName('static', 'debug'). Для получения бинарного файла используем getBinary, например, getBinary('executable', 'release') или getBinary('static', 'debug').

Поддержка CInterop

Поскольку Kotlin/Native обеспечивает совместимость с родными языками, существует DSL, позволяющий настроить эту функцию для конкретной компиляции.

Компиляция может взаимодействовать с несколькими родными библиотеками. Взаимодействие с каждым из них можно настроить в блоке компиляции cinterops:

// In the scope of a Kotlin/Native target's compilation:
cinterops {
    myInterop {
        // Def-file describing the native API.
        // The default path is src/nativeInterop/cinterop/<interop-name>.def
        defFile project.file("def-file.def")

        // Package to place the Kotlin API generated.
        packageName 'org.sample'

        // Options to be passed to compiler by cinterop tool.
        compilerOpts '-Ipath/to/headers'

        // Directories to look for headers.
        includeDirs {
            // Directories for header search (an analogue of the -I<path> compiler option).
            allHeaders 'path1', 'path2'

            // Additional directories to search headers listed in the 'headerFilter' def-file option.
            // -headerFilterAdditionalSearchPrefix command line option analogue.
            headerFilterOnly 'path1', 'path2'
        }
        // A shortcut for includeDirs.allHeaders.
        includeDirs "include/directory", "another/directory"
    }

    anotherInterop { /* ... */ }
}

Часто для бинарного файла, использующего собственную библиотеку, необходимо указать параметры компоновщика для конкретной платформы. Это можно сделать с использованием метода DSL linkerOpts компиляции Kotlin/Native:

compilations.main {
    linkerOpts '-L/lib/search/path -L/another/search/path -lmylib'
}