Как импортировать локальные пакеты без 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. Причина, по которой мне нужен этот вид функциональности, заключается в крупных проектах; Я не хочу ссылаться на несколько других пакетов или хранить их в одном большом файле.

Flimzy
источник
2
Вы должны поместить исходные файлы для каждого двоичного файла в отдельный каталог.
fuz
Все .goфайлы в одном каталоге являются частью одного и того же пакета, и вам не нужны importфайлы в одном и том же пакете (то есть в одном и том же каталоге). Вы упомянули работу вне GOPATH, которая является одной из возможностей новой системы модулей Go. Этот ответ охватывает структуру модуля, импорт локальных пакетов, организацию пакетов внутри модуля, наличие или отсутствие нескольких модулей в одном репозитории и т. Д.
типично182
3
И это поведение хорошо со всеми? Что вы в принципе не можете импортировать свои локальные подпакеты, если не укажете полный git/repo/to/my/projectпуть? Я просто не вижу причины, по которой кто-то хотел бы такого поведения. Что если вы переместите свой проект в другое место (например, изображение Docker), вам нужно снова изменить все пути? Я ищу ответы, почему это так сложно.
Милосмны
@milosmns см. мой ответ stackoverflow.com/a/60915633/175071
Тимо Хуовинен

Ответы:

176

Перейти резюме управления зависимостями:

  • vgo если ваша версия go: x >= go 1.11
  • depили vendorесли ваша версия go:go 1.6 >= x < go 1.11
  • Вручную, если ваша версия go: x < go 1.6

Редактировать 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"in 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
Относительные пути импорта - плохая идея .
Дейв С
1
если #golang предоставляет «пространство имен», я могу согласиться с вами, что «относительный путь импорта» или «подпакеты» являются плохими идеями ».
mission.liao
1
Имя функции должно начинаться с ключевого слова
Capitilized
71

Там нет такого понятия, как "местный пакет". Организация пакетов на диске ортогональна любым родительским / дочерним отношениям пакетов. Единственная реальная иерархия, образованная пакетами, - это дерево зависимостей, которое в общем случае не отражает дерево каталогов.

Просто используйте

import "myproject/packageN"

и не боритесь с системой сборки без веской причины. Сохранение дюжины символов на импорт в любой нетривиальной программе не является хорошей причиной, потому что, например, проекты с относительными путями импорта не являются go-gettable.

Концепция путей импорта имеет несколько важных свойств:

  • Пути импорта могут быть глобально уникальными.
  • В сочетании с GOPATH путь импорта может быть однозначно переведен в путь каталога.
  • Любой путь к каталогу в GOPATH может быть однозначно переведен в путь импорта.

Все вышеперечисленное разрушается при использовании относительных путей импорта. Не делать это.

PS: в унаследованном коде мало мест в тестах компилятора Go, в которых используется относительный импорт. ATM, это единственная причина, по которой относительный импорт поддерживается вообще.

ZZZZ
источник
2
Я рекомендую взглянуть на это вступительное видео для лучшего понимания пакетов и GOPATH . 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 из проекта1 и проекта2 с помощью

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 , можно также запустить проект за пределами GOPATH.

Импортировать локальный пакет:

Создайте демопроект папки и выполните следующую команду для создания файла go.mod

go mod init demoproject

У меня есть структура проекта, как показано ниже в каталоге 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 build в папке 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. Другими словами, все файлы с подкаталогом должны быть одного пакета.

Кроме того, вы можете вкладывать больше подкаталогов, если в импорте вы указали полный путь к родительской папке.

Homan
источник
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 вы заменяете это имя внешнего пакета локальной папкой.

Путь к папке может быть полным или относительным "/ path / to / bar" или "../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 -в-ваш-местный-модуль /

Тимо Хуовинен
источник