Як імпортувати локальні пакети без gopath


171

Я використовував, GOPATHале для цього поточного питання, з яким я стикаюся, це не допомагає. Я хочу мати можливість створювати пакети, специфічні для проекту:

myproject/
├── binary1.go
├── binary2.go
├── package1.go
└── package2.go

Я спробував кілька способів, але як мені домогтися package1.goроботи в тому binary1.goчи binary2.goіншому?

Наприклад; Я хочу бути в змозі, import "package1"а потім бути в змозі запустити, go build binary1.goі все працює нормально, не викидаючи помилку, яку пакет не можна знайти на GOROOTабо GOPATH. Причина, чому мені потрібен такий функціонал, - це для масштабних проектів; Мені не хочеться посилатися на кілька інших пакетів або зберігати їх в одному великому файлі.


2
Ви повинні помістити вихідні файли для кожного бінарного файлу у свою власну директорію.
fuz

Усі .goфайли в одному каталозі є частиною одного пакету, і вам не потрібно importфайли в одному пакеті (тобто, в одному каталозі). Ви згадали про роботу поза GOPATH, що є однією з можливостей нової системи модулів Go. Ця відповідь охоплює структуру модулів, імпорт локальних пакетів, упорядкування пакетів в модулі, чи є декілька модулів в одному сховищі тощо.
типово182

3
І така поведінка з усіма нормальна? Що ви в основному не можете імпортувати локальні підпакети, якщо не вказати весь git/repo/to/my/projectшлях? Я просто не бачу причини, чому хтось захотів би такої поведінки. Що робити, якщо ви перемістите проект в інше місце (тобто зображення Докера), вам потрібно буде знову змінити всі шляхи? Я шукаю відповіді, чому це так складно.
milosmns

@milosmns побачити моя відповідь stackoverflow.com/a/60915633/175071
Тімо Huovinen

Відповіді:


176

Підсумок управління залежностями:

  • vgo якщо ваша версія go: x >= go 1.11
  • depабо vendorякщо ваша версія go:go 1.6 >= x < go 1.11
  • Вручну, якщо ваша версія go: x < go 1.6

Edit 3: Go 1.11 має функцію, vgoяка замінить dep .

Для використання vgoдив. Документацію щодо модулів . TLDR нижче:

export GO111MODULE=on
go mod init
go mod vendor # if you have vendor/ folder, will automatically integrate
go build

Цей метод створює файл, який називається go.modу вашому каталозі проектів. Потім ви можете створити свій проект за допомогою go build. Якщо GO111MODULE=autoвстановлено, ваш проект не може бути включений $GOPATH.


Редагувати 2: метод продавця все ще діє та працює без проблем. vendorбагато в чому ручний процес, завдяки цьому depі vgoбули створені.


Редагувати 1: Хоча мій старий спосіб працює, це вже не «правильний» спосіб. Ви повинні використовувати можливості постачальникаvgo , або dep(поки що), які за умовчанням включені в Go 1.6; див . Ви в основному додаєте ваші "зовнішні" або "залежні" пакети в vendorкаталог; після компіляції компілятор спочатку буде використовувати ці пакети.


Знайдено. Мені вдалося імпортувати локальний пакунок GOPATH, створивши підпапку, package1а потім імпортуючи import "./package1"в binary1.goі binary2.goтакі сценарії:

binary1.go

...
import (
        "./package1"
      )
...

Тож моя поточна структура каталогу виглядає приблизно так:

myproject/
├── binary1.go
├── binary2.go
├── package1/
   └── package1.go
└── package2.go

Я також повинен зазначити, що відносні шляхи (принаймні, у ході 1.5) також працюють; наприклад:

import "../packageX"

4
Це працює добре, поки у вас є дві підпапки, в яких одна посилається на іншу. Наприклад, якщо package2 також був підпапкою і їй потрібен package1, система розпадається.
Карл

7
import "../package1"
Фелікс Рабе

12
Відносні шляхи імпорту - погана ідея .
Дейв C

1
якщо #golang надає "простір імен", я можу погодитися з вами, що "відносний шлях імпорту" або "підпакети" - це погані ідеї ".
місія.liao

1
Назва функції повинна починатися з великого слова з великої літери
kokemomuke

71

Немає такого поняття, як "локальний пакет". Організація пакетів на диску є ортогональною для будь-яких стосунків батьків / дитини. Єдина реальна ієрархія, що формується пакетами, - це дерево залежності, яке в загальному випадку не відображає дерево каталогів.

Просто використовуйте

import "myproject/packageN"

і не воюйте з системою збирання без поважних причин. Збереження десятка символів на імпорт у будь-якій нетривіальній програмі не є вагомою причиною, тому що, наприклад, проекти з відносними шляхами імпорту не можна отримати.

Концепція шляхів імпорту має деякі важливі властивості:

  • Шляхи імпорту можуть бути унікальними у всьому світі.
  • У поєднанні з GOPATH шлях імпорту може бути однозначно переведений у шлях до каталогу.
  • Будь-який шлях до каталогу в GOPATH можна однозначно перекласти на шлях імпорту.

Все вищезазначене руйнується за допомогою відносних шляхів імпорту. Не роби цього.

PS: У тестах компілятора Go мало місць у застарілому коді, які використовують відносний імпорт. Банкомати, це єдина причина, чому відносний імпорт взагалі підтримується.


2
Рекомендую переглянути це вступне відео для кращого розуміння пакунків та ГОПАТУ . youtube.com/watch?v=XCsL89YtqCs
Джошуа Пінтер

7
Я думаю, це погано радить. Якщо ви, наприклад, використовуєте gopkg.in для версії, наприклад, вам не пощастило з абсолютними шляхами імпорту ваших "міні" пакунків, як описано вище. Або ви зламаєте джерело репо, або переобладнаний стає марним.
Грег

import "myproject/packageN". myprojectце ім'я папки, яка містить мій проект?
securecurve

Це абсолютно неправильно, як я зараз його використовую з приватними сховищами?
агілоб

44

Можливо, ви намагаєтеся модулювати пакет. Я припускаю, що package1і package2є, певним чином, частиною одного пакету, але для читабельності ви розділяєте їх на кілька файлів.

Якщо попередній випадок був вашим, ви можете використовувати те саме ім’я пакета у цих файлах з декількома файлами, і це буде схоже на те, що був би той самий файл.

Це приклад:

add.go

package math

func add(n1, n2 int) int {
   return n1 + n2
}

subtract.go

package math

func subtract(n1, n2 int) int {
    return n1 - n2
}

donothing.go

package math

func donothing(n1, n2 int) int {
    s := add(n1, n2)
    s = subtract(n1, n2)
    return s
}

Я не експерт з Go, і це моє перше повідомлення в StackOveflow, тому, якщо у вас є поради, він буде добре сприйнятий.


23

У мене є подібна проблема, і рішення, яке я зараз використовую, використовує модулі Go 1.11. У мене така структура

- projects
  - go.mod
  - go.sum
  - project1
    - main.go
  - project2
    - main.go
  - package1
    - lib.go
  - package2
    - lib.go

І я можу імпортувати package1 та package2 з project1 та project2 за допомогою

import (
    "projects/package1"
    "projects/package2"
)

Після бігу go mod init projects. Я можу використовувати go buildз каталогів project1 та project2 або можу робити go build -o project1/exe project1/*.goз каталогу проектів.

Недоліком цього методу є те, що всі ваші проекти в кінцевому підсумку діють однаковий список залежностей у go.mod. Я все ще шукаю рішення цієї проблеми, але, схоже, це може бути принциповим.


9

З моменту впровадження go.mod , я думаю, що управління локальним і зовнішнім пакетами стає простішим. Використовуючи go.mod , також можна мати проект go за межами GOPATH.

Імпортувати локальний пакет:

Створіть демопроект папки та запустіть наступну команду, щоб створити файл go.mod

go mod init demoproject

У мене така структура проекту, як нижче, всередині каталогу демопроектів .

├── go.mod
└── src
    ├── main.go
    └── model
        └── model.go

Для демонстраційної мети вставте наступний код у файл model.go .

package model

type Employee struct {
    Id          int32
    FirstName   string
    LastName    string
    BadgeNumber int32
}

У main.go я імпортував модель Employee, посилаючись на "demoproject / src / model"

package main

import (
    "demoproject/src/model"
    "fmt"
)

func main() {
    fmt.Printf("Main Function")

    var employee = model.Employee{
        Id:          1,
        FirstName:   "First name",
        LastName:    "Last Name",
        BadgeNumber: 1000,
    }
    fmt.Printf(employee.FirstName)
}

Імпорт зовнішньої залежності:

Просто запустіть go getкоманду всередині каталогу проектів.

Наприклад:

go get -u google.golang.org/grpc

Він повинен містити залежність модуля у файлі go.mod

module demoproject

go 1.13

require (
    golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa // indirect
    golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9 // indirect
    golang.org/x/text v0.3.2 // indirect
    google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150 // indirect
    google.golang.org/grpc v1.26.0 // indirect
)

https://blog.golang.org/using-go-modules


can't load package: package .: no Go files in...(ідіть в папку go.mod)
Sebi2020

Така банальність, але мені знадобилося незручне кількість часу, щоб знайти відповідь, і ваш пост був найбільш розбірливим і корисним. Дякую!
Гарольд Кавендіш

8

Щоб додати "локальний" пакет до свого проекту, додайте папку (наприклад, "ім'я_пакету"). І помістіть ваші файли реалізації у цю папку.

src/github.com/GithubUser/myproject/
 ├── main.go
 └───package_name
       └── whatever_name1.go
       └── whatever_name2.go

У вашому package mainзробіть це:

import "github.com/GithubUser/myproject/package_name"

Де package_nameім'я папки, і воно повинно відповідати імені пакета, який використовується у файлах файлу what_name1.go та what_name2.go. Іншими словами, всі файли з підкаталогом повинні мати один пакет.

Ви можете додатково вкладати більше підкаталогів до тих пір, поки в імпорті буде вказано весь шлях до батьківської папки.


2
Це чудова пропозиція, за винятком того, що під час будь-якої ядра паніки слід стека, який викидається з двійкового файлу, показує, наприклад, шлях github.com, не завжди найбільш бажану поведінку. Існують прапори, щоб придушити це, але це не повинно бути просто для досягнення простої організації пакунків, і я виявив, що він невдалий.
Кенні Пауерс

package myproject/package_name is not in GOROOT (/usr/lib/go-1.14/src/myproject/package_name)
Sebi2020

3

Можна використовувати replace

go modo init example.com/my/foo

foo / go.mod

module example.com/my/foo

go 1.14

replace example.com/my/bar => /path/to/bar

require example.com/my/bar v1.0.0

foo / main.go

package main
import "example.com/bar"

func main() {
    bar.MyFunc()
}

бар / go.mod

module github.com/my/bar

go 1.14

бар / fn.go

package github.com/my/bar

import "fmt"

func MyFunc() {
    fmt.Printf("hello")
}

Імпорт локального пакету - це як імпорт зовнішньої пакунки

окрім файлу go.mod, ви замінюєте це зовнішнє ім'я пакету на локальну папку.

Шлях до папки може бути повним або відносним "/ шлях / до / бар" або "../bar"

https://github.com/golang/go/wiki/Modules#when-should-i-use-the-replace-directive https://thewebivore.com/using-replace-in-go-mod-to-point -to-your-local-module /

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.