Jak zaimportować pakiety lokalne bez gopath


171

Użyłem, GOPATHale w tym bieżącym problemie, przed którym stoję, nie pomaga. Chcę mieć możliwość tworzenia pakietów specyficznych dla projektu:

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

Próbowałem na wiele sposobów, ale jak mam zacząć package1.gopracować w tym binary1.golub innym binary2.goi tak dalej?

Na przykład; Chcę mieć możliwość, import "package1"a następnie móc działać go build binary1.goi wszystko działa dobrze bez wyrzucania błędu, że nie można znaleźć pakietu na GOROOTlub GOPATH. Powodem, dla którego potrzebuję tego rodzaju funkcjonalności, są projekty na dużą skalę; Nie chcę odwoływać się do wielu innych pakietów ani przechowywać ich w jednym dużym pliku.


2
Powinieneś umieścić pliki źródłowe każdego pliku binarnego w jego własnym katalogu.
fuz

Wszystkie .gopliki w jednym katalogu są częścią tego samego pakietu i nie potrzebujesz importplików w tym samym pakiecie (tj. W tym samym katalogu). Wspomniał Pan o pracy poza GOPATH, co jest jedną z możliwości nowego systemu modułów Go. Ta odpowiedź dotyczy struktury modułu, importowania pakietów lokalnych, organizowania pakietów w module, czy mieć wiele modułów w jednym repozytorium itp.
typowo 182

3
I to zachowanie jest w porządku dla wszystkich? Że w zasadzie nie możesz importować lokalnych pakietów podrzędnych, chyba że określisz całą git/repo/to/my/projectścieżkę? Po prostu nie widzę powodu, dla którego ktokolwiek miałby chcieć takiego zachowania. Co jeśli przeniesiesz swój projekt w inne miejsce (np. Obraz Dockera), będziesz musiał ponownie zmienić wszystkie ścieżki? Szukam odpowiedzi, dlaczego to takie skomplikowane.
milosmns

@milosmns zobacz moją odpowiedź stackoverflow.com/a/60915633/175071
Timo Huovinen

Odpowiedzi:


176

Podsumowanie zarządzania zależnościami Idź:

  • vgo jeśli twoja wersja go to: x >= go 1.11
  • dep lub vendor jeśli Twoja wersja go to:go 1.6 >= x < go 1.11
  • Ręcznie, jeśli Twoja wersja go to: x < go 1.6

Edycja 3: Go 1.11 ma funkcję, vgoktóra zastąpi dep .

Aby skorzystać vgo, zobacz dokumentację modułów . TLDR poniżej:

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

Ta metoda tworzy plik o nazwie go.modw katalogu projektów. Następnie możesz zbudować swój projekt za pomocą go build. Jeśli GO111MODULE=autojest ustawiona, Twój projekt nie może być w formacie $GOPATH.


Edycja 2: Metoda sprzedaży jest nadal ważna i działa bez problemu. vendorjest w dużej mierze procesem ręcznym, z tego powodu depi vgozostały stworzone.


Edycja 1: Chociaż mój stary sposób działa, nie jest to już „poprawny” sposób. Powinieneś używać możliwości dostawcyvgo lub dep(na razie), które są domyślnie włączone w Go 1.6; zobacz . Zasadniczo dodajesz swoje „zewnętrzne” lub „zależne” pakiety w vendorkatalogu; po kompilacji kompilator najpierw użyje tych pakietów.


Znaleziony. Udało mi się zaimportować pakiet lokalny za GOPATHpomocą, tworząc podfolder, package1a następnie importując za pomocą import "./package1"in binary1.goibinary2.go skrypty takie jak ten:

binary1.go

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

Więc moja obecna struktura katalogów wygląda następująco:

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

Powinienem również zauważyć, że ścieżki względne (przynajmniej w go 1.5) również działają; na przykład:

import "../packageX"

4
Działa to dobrze, dopóki nie masz dwóch podfolderów, z których jeden odnosi się do drugiego. Na przykład, jeśli pakiet2 był również podfolderem i wymagał pakietu pakiet1, system się zepsuje.
Carl

7
import "../package1"
Felix Rabe

12
Względne ścieżki importu to zły pomysł .
Dave C

1
jeśli #golang zapewnia „przestrzeń nazw”, zgadzam się z Tobą, że „względna ścieżka importu” lub „pakiety podrzędne” to zły pomysł ”.
mission.liao

1
Nazwa funkcji powinna zaczynać się od słowa kluczowego z wielką literą
kokemomuke

71

Nie ma czegoś takiego jak „pakiet lokalny”. Organizacja pakietów na dysku jest ortogonalna do wszelkich relacji nadrzędny / podrzędny pakietów. Jedyną prawdziwą hierarchią utworzoną przez pakiety jest drzewo zależności, które w ogólnym przypadku nie odzwierciedla drzewa katalogów.

Po prostu użyj

import "myproject/packageN"

i nie walcz z systemem kompilacji bez powodu. Zapisywanie kilkunastu znaków na import w dowolnym nietrywialnym programie nie jest dobrym powodem, ponieważ na przykład projekty ze względnymi ścieżkami importu nie są dostępne do pobrania.

Pojęcie ścieżek importu ma kilka ważnych właściwości:

  • Ścieżki importu mogą być unikalne w skali globalnej.
  • W połączeniu z GOPATH, ścieżka importu może być jednoznacznie przetłumaczona na ścieżkę katalogu.
  • Każda ścieżka katalogu w GOPATH może zostać jednoznacznie przetłumaczona na ścieżkę importu.

Wszystkie powyższe są zrujnowane przez użycie względnych ścieżek importu. Nie rób tego.

PS: Jest kilka miejsc w starszym kodzie testów kompilatora Go, które używają importu względnego. ATM, to jedyny powód, dla którego import względny jest w ogóle obsługiwany.


2
Polecam obejrzenie tego filmu wprowadzającego, aby lepiej zrozumieć pakiety i GOPATH . youtube.com/watch?v=XCsL89YtqCs
Joshua Pinter

7
Myślę, że to zła rada. Jeśli na przykład użyjesz gopkg.in do wersjonowania, nie masz szczęścia do bezwzględnych ścieżek importu dla "mini" paczek, jak opisano powyżej. Albo zepsujesz repozytorium źródłowe, albo wersja z wersjami stanie się bezużyteczna.
Greg

import "myproject/packageN". myprojectto nazwa folderu zawierającego mój projekt?
securecurve

To całkowicie błędne, jak teraz mogę go używać z prywatnymi repozytoriami?
agilob

44

Być może próbujesz zmodularyzować swój pakiet. Zakładam, że package1ipackage2 są w pewnym sensie częścią tego samego pakietu, ale dla czytelności dzielisz je na wiele plików.

Jeśli poprzedni przypadek był twój, możesz użyć tej samej nazwy pakietu w tych wielokrotnych plikach i będzie tak, jakby istniał ten sam plik.

To jest przykład:

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
}

Nie jestem ekspertem w Go i to jest mój pierwszy post w StackOveflow, więc jeśli masz jakieś rady, zostanie on dobrze przyjęty.


23

Mam podobny problem i rozwiązanie, z którego obecnie korzystam, wykorzystuje moduły Go 1.11. Mam następującą strukturę

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

Jestem w stanie zaimportować pakiet1 i pakiet2 z projektu1 i projekt2 przy użyciu

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

Po biegu go mod init projects. Mogę korzystać go buildz katalogów project1 i project2 lub mogę to zrobić go build -o project1/exe project1/*.goz katalogu projektów.

Wadą tej metody jest to, że wszystkie projekty kończą na współdzieleniu tej samej listy zależności w go.mod. Wciąż szukam rozwiązania tego problemu, ale wygląda na to, że może to być fundamentalne.


9

Myślę , że od czasu wprowadzenia go.mod zarządzanie pakietami zarówno lokalnymi, jak i zewnętrznymi stało się łatwiejsze. Używając go.mod , można mieć projekt go również poza GOPATH.

Importuj pakiet lokalny:

Utwórz folder demoproject i uruchom następujące polecenie, aby wygenerować plik go.mod

go mod init demoproject

Mam strukturę projektu jak poniżej w katalogu demoproject .

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

W celach demonstracyjnych wstaw następujący kod w pliku model.go .

package model

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

W main.go zaimportowałem model pracownika, odnosząc się do „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)
}

Importuj zależność zewnętrzną:

Po prostu uruchom go getpolecenie w katalogu projektu.

Na przykład:

go get -u google.golang.org/grpc

Powinien zawierać zależność modułów w pliku 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...(przejdź do tworzenia w folderze go.mod)
Sebi2020

Taka banalność, ale żenująco dużo czasu zajęło mi znalezienie odpowiedzi, a Twój post był najbardziej czytelny i przydatny. Dziękuję Ci!
Harold Cavendish

8

Aby dodać pakiet „lokalny” do projektu, dodaj folder (na przykład „nazwa_pakietu”). I umieść pliki implementacji w tym folderze.

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

W swoim package mainzrób to:

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

Gdzie package_namejest nazwa folderu i musi być zgodna z nazwą pakietu używaną w plikach cokolwiek_nazwa1.go i cokolwiek_nazwa2.go. Innymi słowy, wszystkie pliki z podkatalogami powinny należeć do tego samego pakietu.

Możesz dalej zagnieżdżać więcej podkatalogów, o ile w imporcie określisz pełną ścieżkę do folderu nadrzędnego.


2
To dobra sugestia, z tym wyjątkiem, że podczas paniki jądra ślad stosu, który jest zrzucany z pliku binarnego, pokazuje na przykład ścieżkę github.com, nie zawsze jest to najbardziej pożądane zachowanie. Istnieją flagi, które to powstrzymują, ale nie powinno to być konieczne tylko do osiągnięcia prostej organizacji pakietu i odkryłem, że czasami zawodzi.
Kenny Powers,

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

3

Możesz użyć 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()
}

bar / go.mod

module github.com/my/bar

go 1.14

bar / fn.go

package github.com/my/bar

import "fmt"

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

Importowanie pakietu lokalnego przypomina importowanie pakietu zewnętrznego

poza tym, że wewnątrz pliku go.mod zastępujesz zewnętrzną nazwę pakietu folderem lokalnym.

Ścieżka do folderu może być pełna lub względna „/ ścieżka / do / bar” lub „../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 /

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.