shithub: hugo

Download patch

ref: 9f77f93071d836a35b0078d1b349968abddb4a18
parent: 3a8c12418a97230df714488e522f44ce8af685c5
author: bep <[email protected]>
date: Sun Dec 7 14:48:00 EST 2014

Fix various Windows-issues

File handling was broken on Windows. This commit contains a revision of the path handling with separation of file paths and urls where needed.

There may be remaining issues and there may be better ways to do this, but it is easier to start that refactoring job with a set of passing tests.

Fixes #687
Fixes #660

--- a/helpers/general.go
+++ b/helpers/general.go
@@ -20,8 +20,11 @@
 	"fmt"
 	"io"
 	"net"
+	"path/filepath"
 	"strings"
 )
+
+const FilePathSeparator = string(filepath.Separator)
 
 func FindAvailablePort() (*net.TCPAddr, error) {
 	l, err := net.Listen("tcp", ":0")
--- a/helpers/path.go
+++ b/helpers/path.go
@@ -16,6 +16,8 @@
 import (
 	"errors"
 	"fmt"
+	"github.com/spf13/afero"
+	"github.com/spf13/viper"
 	"io"
 	"os"
 	"path/filepath"
@@ -22,9 +24,6 @@
 	"regexp"
 	"strings"
 	"unicode"
-
-	"github.com/spf13/afero"
-	"github.com/spf13/viper"
 )
 
 var sanitizeRegexp = regexp.MustCompile("[^a-zA-Z0-9./_-]")
@@ -173,13 +172,18 @@
 	ext = filepath.Ext(in)
 	base := filepath.Base(in) // path.Base strips any trailing slash!
 
+	return FileAndExtSep(in, ext, base, FilePathSeparator), ext
+}
+
+func FileAndExtSep(in, ext, base, pathSeparator string) (name string) {
+
 	// No file name cases. These are defined as:
-	// 1. any "in" path that ends in a os.PathSeparator i.e. "/" on linux
-	// 2. any "base" consisting of just an os.PathSeparator
+	// 1. any "in" path that ends in a pathSeparator
+	// 2. any "base" consisting of just an pathSeparator
 	// 3. any "base" consisting of just an empty string
 	// 4. any "base" consisting of just the current directory i.e. "."
 	// 5. any "base" consisting of just the parent directory i.e. ".."
-	if (strings.LastIndex(in, string(os.PathSeparator)) == len(in)-1) || base == "" || base == "." || base == ".." || base == string(os.PathListSeparator) {
+	if (strings.LastIndex(in, pathSeparator) == len(in)-1) || base == "" || base == "." || base == ".." || base == pathSeparator {
 		name = "" // there is NO filename
 	} else if ext != "" { // there was an Extension
 		// return the filename minus the extension (and the ".")
@@ -190,6 +194,7 @@
 		name = base
 	}
 	return
+
 }
 
 func GetRelativePath(path, base string) (final string, err error) {
@@ -203,7 +208,6 @@
 	if err != nil {
 		return "", err
 	}
-	name = filepath.ToSlash(name)
 	return name, nil
 }
 
@@ -210,7 +214,7 @@
 // Given a source path, determine the section
 // A section is the part between the root slash and the second slash or before the first slash
 func GuessSection(in string) string {
-	parts := strings.Split(in, "/")
+	parts := strings.Split(in, FilePathSeparator)
 	// This will include an empty entry before and after paths with leading and trailing slashes
 	// eg... /sect/one/ -> ["", "sect", "one", ""]
 
@@ -256,7 +260,7 @@
 	if filepath.Ext(in) == "" {
 		// /section/name/  -> /section/name/index.html
 		if len(in) < 2 {
-			return "/"
+			return FilePathSeparator
 		}
 		return filepath.Join(filepath.Clean(in), "index.html")
 	} else {
--- a/helpers/path_test.go
+++ b/helpers/path_test.go
@@ -4,7 +4,7 @@
 	"fmt"
 	"io/ioutil"
 	"os"
-	"path"
+	"path/filepath"
 	"strconv"
 	"strings"
 	"testing"
@@ -119,7 +119,7 @@
 	}
 
 	for i, d := range data {
-		output := ReplaceExtension(d.input, d.newext)
+		output := ReplaceExtension(filepath.FromSlash(d.input), d.newext)
 		if d.expected != output {
 			t.Errorf("Test %d failed. Expected %q got %q", i, d.expected, output)
 		}
@@ -139,8 +139,8 @@
 		{"../", true},
 		{"./..", true},
 		{"./../", true},
-		{"/tmp", true},
-		{"/tmp/", true},
+		{os.TempDir(), true},
+		{os.TempDir() + FilePathSeparator, true},
 		{"/", true},
 		{"/some-really-random-directory-name", false},
 		{"/some/really/random/directory/name", false},
@@ -149,7 +149,7 @@
 	}
 
 	for i, d := range data {
-		exists, _ := DirExists(d.input, new(afero.OsFs))
+		exists, _ := DirExists(filepath.FromSlash(d.input), new(afero.OsFs))
 		if d.expected != exists {
 			t.Errorf("Test %d failed. Expected %t got %t", i, d.expected, exists)
 		}
@@ -366,8 +366,8 @@
 		input, expected string
 	}
 	data := []test{
-		{os.TempDir(), path.Clean(os.TempDir())}, // TempDir has trailing slash
-		{"/banana/../dir/", "/dir"},
+		{os.TempDir(), filepath.Clean(os.TempDir())}, // TempDir has trailing slash
+		{filepath.FromSlash("/banana/../dir/"), filepath.FromSlash("/dir")},
 	}
 
 	for i, d := range data {
@@ -400,7 +400,7 @@
 	}
 
 	for i, d := range data {
-		output := Filename(d.input)
+		output := Filename(filepath.FromSlash(d.input))
 		if d.expected != output {
 			t.Errorf("Test %d failed. Expected %q got %q", i, d.expected, output)
 		}
@@ -429,7 +429,7 @@
 	}
 
 	for i, d := range data {
-		file, ext := FileAndExt(d.input)
+		file, ext := FileAndExt(filepath.FromSlash(d.input))
 		if d.expectedFile != file {
 			t.Errorf("Test %d failed. Expected filename %q got %q.", i, d.expectedFile, file)
 		}
@@ -467,7 +467,7 @@
 	}
 
 	for i, d := range data {
-		expected := GuessSection(d.input)
+		expected := GuessSection(filepath.FromSlash(d.input))
 		if d.expected != expected {
 			t.Errorf("Test %d failed. Expected %q got %q", i, d.expected, expected)
 		}
--- a/helpers/url.go
+++ b/helpers/url.go
@@ -15,11 +15,10 @@
 
 import (
 	"fmt"
+	"github.com/PuerkitoBio/purell"
 	"net/url"
-	"path/filepath"
+	"path"
 	"strings"
-
-	"github.com/PuerkitoBio/purell"
 )
 
 func SanitizeUrl(in string) string {
@@ -68,7 +67,7 @@
 		panic(fmt.Errorf("Can't make permalink from absolute link %q", plink))
 	}
 
-	base.Path = filepath.Join(base.Path, p.Path)
+	base.Path = path.Join(base.Path, p.Path)
 
 	// path.Join will strip off the last /, so put it back if it was there.
 	if strings.HasSuffix(p.Path, "/") && !strings.HasSuffix(base.Path, "/") {
@@ -84,7 +83,7 @@
 		return x
 	} else {
 		x := PrettifyUrl(SanitizeUrl(in))
-		if filepath.Ext(x) == ".xml" {
+		if path.Ext(x) == ".xml" {
 			return x
 		}
 		url, err := purell.NormalizeURLString(x, purell.FlagAddTrailingSlash)
@@ -98,10 +97,10 @@
 
 // Don't Return /index.html portion.
 func PrettifyUrl(in string) string {
-	x := PrettifyPath(in)
+	x := PrettifyUrlPath(in)
 
-	if filepath.Base(x) == "index.html" {
-		return filepath.Dir(x)
+	if path.Base(x) == "index.html" {
+		return path.Dir(x)
 	}
 
 	if in == "" {
@@ -111,21 +110,43 @@
 	return x
 }
 
+// /section/name.html -> /section/name/index.html
+// /section/name/  -> /section/name/index.html
+// /section/name/index.html -> /section/name/index.html
+func PrettifyUrlPath(in string) string {
+	if path.Ext(in) == "" {
+		// /section/name/  -> /section/name/index.html
+		if len(in) < 2 {
+			return "/"
+		}
+		return path.Join(path.Clean(in), "index.html")
+	} else {
+		name, ext := ResourceAndExt(in)
+		if name == "index" {
+			// /section/name/index.html -> /section/name/index.html
+			return path.Clean(in)
+		} else {
+			// /section/name.html -> /section/name/index.html
+			return path.Join(path.Dir(in), name, "index"+ext)
+		}
+	}
+}
+
 // /section/name/index.html -> /section/name.html
 // /section/name/  -> /section/name.html
 // /section/name.html -> /section/name.html
 func Uglify(in string) string {
-	if filepath.Ext(in) == "" {
+	if path.Ext(in) == "" {
 		if len(in) < 2 {
 			return "/"
 		}
 		// /section/name/  -> /section/name.html
-		return filepath.Clean(in) + ".html"
+		return path.Clean(in) + ".html"
 	} else {
-		name, ext := FileAndExt(in)
+		name, ext := ResourceAndExt(in)
 		if name == "index" {
 			// /section/name/index.html -> /section/name.html
-			d := filepath.Dir(in)
+			d := path.Dir(in)
 			if len(d) > 1 {
 				return d + ext
 			} else {
@@ -133,7 +154,15 @@
 			}
 		} else {
 			// /section/name.html -> /section/name.html
-			return filepath.Clean(in)
+			return path.Clean(in)
 		}
 	}
+}
+
+// Same as FileAndExt, but for Urls
+func ResourceAndExt(in string) (name string, ext string) {
+	ext = path.Ext(in)
+	base := path.Base(in)
+
+	return FileAndExtSep(in, ext, base, "/"), ext
 }
--- a/helpers/url_test.go
+++ b/helpers/url_test.go
@@ -69,14 +69,14 @@
 }
 
 func TestPretty(t *testing.T) {
-	assert.Equal(t, PrettifyPath("/section/name.html"), "/section/name/index.html")
-	assert.Equal(t, PrettifyPath("/section/sub/name.html"), "/section/sub/name/index.html")
-	assert.Equal(t, PrettifyPath("/section/name/"), "/section/name/index.html")
-	assert.Equal(t, PrettifyPath("/section/name/index.html"), "/section/name/index.html")
-	assert.Equal(t, PrettifyPath("/index.html"), "/index.html")
-	assert.Equal(t, PrettifyPath("/name.xml"), "/name/index.xml")
-	assert.Equal(t, PrettifyPath("/"), "/")
-	assert.Equal(t, PrettifyPath(""), "/")
+	assert.Equal(t, PrettifyUrlPath("/section/name.html"), "/section/name/index.html")
+	assert.Equal(t, PrettifyUrlPath("/section/sub/name.html"), "/section/sub/name/index.html")
+	assert.Equal(t, PrettifyUrlPath("/section/name/"), "/section/name/index.html")
+	assert.Equal(t, PrettifyUrlPath("/section/name/index.html"), "/section/name/index.html")
+	assert.Equal(t, PrettifyUrlPath("/index.html"), "/index.html")
+	assert.Equal(t, PrettifyUrlPath("/name.xml"), "/name/index.xml")
+	assert.Equal(t, PrettifyUrlPath("/"), "/")
+	assert.Equal(t, PrettifyUrlPath(""), "/")
 	assert.Equal(t, PrettifyUrl("/section/name.html"), "/section/name")
 	assert.Equal(t, PrettifyUrl("/section/sub/name.html"), "/section/sub/name")
 	assert.Equal(t, PrettifyUrl("/section/name/"), "/section/name")
--- a/hugolib/page.go
+++ b/hugolib/page.go
@@ -17,13 +17,6 @@
 	"bytes"
 	"errors"
 	"fmt"
-	"html/template"
-	"io"
-	"net/url"
-	"path/filepath"
-	"strings"
-	"time"
-
 	"github.com/spf13/cast"
 	"github.com/spf13/hugo/helpers"
 	"github.com/spf13/hugo/hugofs"
@@ -32,6 +25,13 @@
 	"github.com/spf13/hugo/tpl"
 	jww "github.com/spf13/jwalterweatherman"
 	"github.com/spf13/viper"
+	"html/template"
+	"io"
+	"net/url"
+	"path"
+	"path/filepath"
+	"strings"
+	"time"
 )
 
 type Page struct {
@@ -197,7 +197,7 @@
 	// Add type/layout.html
 	for i := range t {
 		search := t[:len(t)-i]
-		layouts = append(layouts, fmt.Sprintf("%s/%s.html", strings.ToLower(filepath.Join(search...)), layout))
+		layouts = append(layouts, fmt.Sprintf("%s/%s.html", strings.ToLower(path.Join(search...)), layout))
 	}
 
 	// Add _default/layout.html
@@ -250,7 +250,7 @@
 
 func (p *Page) permalink() (*url.URL, error) {
 	baseUrl := string(p.Site.BaseUrl)
-	dir := strings.TrimSpace(p.Source.Dir())
+	dir := strings.TrimSpace(filepath.ToSlash(p.Source.Dir()))
 	pSlug := strings.TrimSpace(p.Slug)
 	pUrl := strings.TrimSpace(p.Url)
 	var permalink string
@@ -269,10 +269,10 @@
 		// fmt.Printf("have a section override for %q in section %s → %s\n", p.Title, p.Section, permalink)
 	} else {
 		if len(pSlug) > 0 {
-			permalink = helpers.UrlPrep(viper.GetBool("UglyUrls"), filepath.Join(dir, p.Slug+"."+p.Extension()))
+			permalink = helpers.UrlPrep(viper.GetBool("UglyUrls"), path.Join(dir, p.Slug+"."+p.Extension()))
 		} else {
 			_, t := filepath.Split(p.Source.LogicalName())
-			permalink = helpers.UrlPrep(viper.GetBool("UglyUrls"), filepath.Join(dir, helpers.ReplaceExtension(strings.TrimSpace(t), p.Extension())))
+			permalink = helpers.UrlPrep(viper.GetBool("UglyUrls"), path.Join(dir, helpers.ReplaceExtension(strings.TrimSpace(t), p.Extension())))
 		}
 	}
 
@@ -674,6 +674,7 @@
 		if strings.HasSuffix(outfile, "/") {
 			outfile = outfile + "index.html"
 		}
+		outfile = filepath.FromSlash(outfile)
 		return
 	}
 
@@ -685,6 +686,7 @@
 			if strings.HasSuffix(outfile, "/") {
 				outfile += "index.html"
 			}
+			outfile = filepath.FromSlash(outfile)
 			return
 		}
 	}
--- a/hugolib/pageGroup_test.go
+++ b/hugolib/pageGroup_test.go
@@ -2,6 +2,7 @@
 
 import (
 	"errors"
+	"path/filepath"
 	"reflect"
 	"testing"
 
@@ -26,7 +27,7 @@
 func preparePageGroupTestPages(t *testing.T) Pages {
 	var pages Pages
 	for _, s := range pageGroupTestSources {
-		p, err := NewPage(s.path)
+		p, err := NewPage(filepath.FromSlash(s.path))
 		if err != nil {
 			t.Fatalf("failed to prepare test page %s", s.path)
 		}
--- a/hugolib/page_permalink_test.go
+++ b/hugolib/page_permalink_test.go
@@ -2,6 +2,7 @@
 
 import (
 	"html/template"
+	"path/filepath"
 	"testing"
 
 	"github.com/spf13/hugo/source"
@@ -48,7 +49,7 @@
 					BaseUrl: test.base,
 				},
 			},
-			Source: Source{File: *source.NewFile(test.file)},
+			Source: Source{File: *source.NewFile(filepath.FromSlash(test.file))},
 		}
 
 		if test.slug != "" {
--- a/hugolib/page_test.go
+++ b/hugolib/page_test.go
@@ -510,10 +510,10 @@
 }
 
 func TestSectionEvaluation(t *testing.T) {
-	page, _ := NewPage("blue/file1.md")
+	page, _ := NewPage(filepath.FromSlash("blue/file1.md"))
 	page.ReadFrom(strings.NewReader(SIMPLE_PAGE))
 	if page.Section() != "blue" {
-		t.Errorf("Section should be %s, got: %s", "blue", page.Section)
+		t.Errorf("Section should be %s, got: %s", "blue", page.Section())
 	}
 }
 
--- a/hugolib/path_seperators_windows_test.go
+++ b/hugolib/path_seperators_windows_test.go
@@ -1,6 +1,7 @@
 package hugolib
 
 import (
+	"github.com/spf13/hugo/tpl"
 	"testing"
 )
 
@@ -10,8 +11,8 @@
 )
 
 func TestTemplatePathSeperator(t *testing.T) {
-	tmpl := new(GoHtmlTemplate)
-	if name := tmpl.generateTemplateNameFrom(win_base, win_path); name != "sub1/index.html" {
+	tmpl := new(tpl.GoHtmlTemplate)
+	if name := tmpl.GenerateTemplateNameFrom(win_base, win_path); name != "sub1/index.html" {
 		t.Fatalf("Template name incorrect.  Expected: %s, Got: %s", "sub1/index.html", name)
 	}
 }
--- a/hugolib/site_show_plan_test.go
+++ b/hugolib/site_show_plan_test.go
@@ -2,11 +2,12 @@
 
 import (
 	"bytes"
-	"strings"
-	"testing"
-
+	"github.com/spf13/hugo/helpers"
 	"github.com/spf13/hugo/source"
 	"github.com/spf13/hugo/target"
+	"path/filepath"
+	"strings"
+	"testing"
 )
 
 const ALIAS_DOC_1 = "---\ntitle: alias doc\naliases:\n  - \"alias1/\"\n  - \"alias-2/\"\n---\naliases\n"
@@ -13,15 +14,15 @@
 
 var fakeSource = []source.ByteSource{
 	{
-		Name:    "foo/bar/file.md",
+		Name:    filepath.FromSlash("foo/bar/file.md"),
 		Content: []byte(SIMPLE_PAGE),
 	},
 	{
-		Name:    "alias/test/file1.md",
+		Name:    filepath.FromSlash("alias/test/file1.md"),
 		Content: []byte(ALIAS_DOC_1),
 	},
 	{
-		Name:    "section/somecontent.html",
+		Name:    filepath.FromSlash("section/somecontent.html"),
 		Content: []byte(RENDER_NO_FRONT_MATTER),
 	},
 }
@@ -36,6 +37,7 @@
 }
 
 func checkShowPlanExpected(t *testing.T, s *Site, expected string) {
+
 	out := new(bytes.Buffer)
 	if err := s.ShowPlan(out); err != nil {
 		t.Fatalf("ShowPlan unexpectedly returned an error: %s", err)
@@ -42,19 +44,17 @@
 	}
 	got := out.String()
 
+	expected = filepath.FromSlash(expected)
+	// hackety hack: alias is an Url
+	expected = strings.Replace(expected, (helpers.FilePathSeparator + " =>"), "/ =>", -1)
+	expected = strings.Replace(expected, "n"+(helpers.FilePathSeparator+"a"), "n/a", -1)
 	gotList := strings.Split(got, "\n")
 	expectedList := strings.Split(expected, "\n")
 
-	for _, x := range gotList {
-		if !stringInSlice(x, expectedList) {
-			t.Errorf("%v %v %v %v", "\nShowPlan expected:\n", expected, "\ngot:\n", got)
-		}
-	}
+	diff := DiffStringSlices(gotList, expectedList)
 
-	for _, x := range expectedList {
-		if !stringInSlice(x, gotList) {
-			t.Errorf("%v %v %v %v", "\nShowPlan expected:\n", expected, "\ngot:\n", got)
-		}
+	if len(diff) > 0 {
+		t.Errorf("Got diff in show plan: %s", diff)
 	}
 }
 
@@ -125,4 +125,27 @@
 		" alias-2/ => ../public/alias-2/index.html\n\n" +
 		"section/somecontent.html (renderer: n/a)\n canonical => ../public/section/somecontent/index.html\n\n"
 	checkShowPlanExpected(t, s, expected)
+}
+
+// DiffStringSlices returns the difference between two string slices.
+// See:
+// http://stackoverflow.com/questions/19374219/how-to-find-the-difference-between-two-slices-of-strings-in-golang
+func DiffStringSlices(slice1 []string, slice2 []string) []string {
+	diffStr := []string{}
+	m := map[string]int{}
+
+	for _, s1Val := range slice1 {
+		m[s1Val] = 1
+	}
+	for _, s2Val := range slice2 {
+		m[s2Val] = m[s2Val] + 1
+	}
+
+	for mKey, mVal := range m {
+		if mVal == 1 {
+			diffStr = append(diffStr, mKey)
+		}
+	}
+
+	return diffStr
 }
--- a/hugolib/site_test.go
+++ b/hugolib/site_test.go
@@ -5,6 +5,7 @@
 	"fmt"
 	"html/template"
 	"io"
+	"path/filepath"
 	"strings"
 	"testing"
 
@@ -191,7 +192,7 @@
 			t.Errorf("Unable to write html: %s", err)
 		}
 
-		file, err := hugofs.DestinationFS.Open("out/index.html")
+		file, err := hugofs.DestinationFS.Open(filepath.FromSlash("out/index.html"))
 		if err != nil {
 			t.Errorf("Unable to open html: %s", err)
 		}
@@ -221,9 +222,9 @@
 	}
 
 	for _, test := range tests {
-		p := pageMust(NewPageFrom(strings.NewReader(test.content), helpers.AbsPathify(test.doc)))
+		p := pageMust(NewPageFrom(strings.NewReader(test.content), helpers.AbsPathify(filepath.FromSlash(test.doc))))
 
-		expected := test.expectedOutFile
+		expected := filepath.FromSlash(test.expectedOutFile)
 
 		if p.TargetPath() != expected {
 			t.Errorf("%s => OutFile  expected: '%s', got: '%s'", test.doc, expected, p.TargetPath())
@@ -238,10 +239,10 @@
 func TestDraftAndFutureRender(t *testing.T) {
 	hugofs.DestinationFS = new(afero.MemMapFs)
 	sources := []source.ByteSource{
-		{"sect/doc1.md", []byte("---\ntitle: doc1\ndraft: true\npublishdate: \"2414-05-29\"\n---\n# doc1\n*some content*")},
-		{"sect/doc2.md", []byte("---\ntitle: doc2\ndraft: true\npublishdate: \"2012-05-29\"\n---\n# doc2\n*some content*")},
-		{"sect/doc3.md", []byte("---\ntitle: doc3\ndraft: false\npublishdate: \"2414-05-29\"\n---\n# doc3\n*some content*")},
-		{"sect/doc4.md", []byte("---\ntitle: doc4\ndraft: false\npublishdate: \"2012-05-29\"\n---\n# doc4\n*some content*")},
+		{filepath.FromSlash("sect/doc1.md"), []byte("---\ntitle: doc1\ndraft: true\npublishdate: \"2414-05-29\"\n---\n# doc1\n*some content*")},
+		{filepath.FromSlash("sect/doc2.md"), []byte("---\ntitle: doc2\ndraft: true\npublishdate: \"2012-05-29\"\n---\n# doc2\n*some content*")},
+		{filepath.FromSlash("sect/doc3.md"), []byte("---\ntitle: doc3\ndraft: false\npublishdate: \"2414-05-29\"\n---\n# doc3\n*some content*")},
+		{filepath.FromSlash("sect/doc4.md"), []byte("---\ntitle: doc4\ndraft: false\npublishdate: \"2012-05-29\"\n---\n# doc4\n*some content*")},
 	}
 
 	siteSetup := func() *Site {
@@ -296,14 +297,14 @@
 func TestSkipRender(t *testing.T) {
 	hugofs.DestinationFS = new(afero.MemMapFs)
 	sources := []source.ByteSource{
-		{"sect/doc1.html", []byte("---\nmarkup: markdown\n---\n# title\nsome *content*")},
-		{"sect/doc2.html", []byte("<!doctype html><html><body>more content</body></html>")},
-		{"sect/doc3.md", []byte("# doc3\n*some* content")},
-		{"sect/doc4.md", []byte("---\ntitle: doc4\n---\n# doc4\n*some content*")},
-		{"sect/doc5.html", []byte("<!doctype html><html>{{ template \"head\" }}<body>body5</body></html>")},
-		{"sect/doc6.html", []byte("<!doctype html><html>{{ template \"head_abs\" }}<body>body5</body></html>")},
-		{"doc7.html", []byte("<html><body>doc7 content</body></html>")},
-		{"sect/doc8.html", []byte("---\nmarkup: md\n---\n# title\nsome *content*")},
+		{filepath.FromSlash("sect/doc1.html"), []byte("---\nmarkup: markdown\n---\n# title\nsome *content*")},
+		{filepath.FromSlash("sect/doc2.html"), []byte("<!doctype html><html><body>more content</body></html>")},
+		{filepath.FromSlash("sect/doc3.md"), []byte("# doc3\n*some* content")},
+		{filepath.FromSlash("sect/doc4.md"), []byte("---\ntitle: doc4\n---\n# doc4\n*some content*")},
+		{filepath.FromSlash("sect/doc5.html"), []byte("<!doctype html><html>{{ template \"head\" }}<body>body5</body></html>")},
+		{filepath.FromSlash("sect/doc6.html"), []byte("<!doctype html><html>{{ template \"head_abs\" }}<body>body5</body></html>")},
+		{filepath.FromSlash("doc7.html"), []byte("<html><body>doc7 content</body></html>")},
+		{filepath.FromSlash("sect/doc8.html"), []byte("---\nmarkup: md\n---\n# title\nsome *content*")},
 	}
 
 	viper.Set("verbose", true)
@@ -337,14 +338,14 @@
 		doc      string
 		expected string
 	}{
-		{"sect/doc1.html", "\n\n<h1 id=\"title:5d74edbb89ef198cd37882b687940cda\">title</h1>\n\n<p>some <em>content</em></p>\n"},
-		{"sect/doc2.html", "<!doctype html><html><body>more content</body></html>"},
-		{"sect/doc3.html", "\n\n<h1 id=\"doc3:28c75a9e2162b8eccda73a1ab9ce80b4\">doc3</h1>\n\n<p><em>some</em> content</p>\n"},
-		{"sect/doc4.html", "\n\n<h1 id=\"doc4:f8e6806123f341b8975509637645a4d3\">doc4</h1>\n\n<p><em>some content</em></p>\n"},
-		{"sect/doc5.html", "<!doctype html><html><head><script src=\"script.js\"></script></head><body>body5</body></html>"},
-		{"sect/doc6.html", "<!doctype html><html><head><script src=\"http://auth/bub/script.js\"></script></head><body>body5</body></html>"},
-		{"doc7.html", "<html><body>doc7 content</body></html>"},
-		{"sect/doc8.html", "\n\n<h1 id=\"title:0ae308ad73e2f37bd09874105281b5d8\">title</h1>\n\n<p>some <em>content</em></p>\n"},
+		{filepath.FromSlash("sect/doc1.html"), "\n\n<h1 id=\"title:5d74edbb89ef198cd37882b687940cda\">title</h1>\n\n<p>some <em>content</em></p>\n"},
+		{filepath.FromSlash("sect/doc2.html"), "<!doctype html><html><body>more content</body></html>"},
+		{filepath.FromSlash("sect/doc3.html"), "\n\n<h1 id=\"doc3:28c75a9e2162b8eccda73a1ab9ce80b4\">doc3</h1>\n\n<p><em>some</em> content</p>\n"},
+		{filepath.FromSlash("sect/doc4.html"), "\n\n<h1 id=\"doc4:f8e6806123f341b8975509637645a4d3\">doc4</h1>\n\n<p><em>some content</em></p>\n"},
+		{filepath.FromSlash("sect/doc5.html"), "<!doctype html><html><head><script src=\"script.js\"></script></head><body>body5</body></html>"},
+		{filepath.FromSlash("sect/doc6.html"), "<!doctype html><html><head><script src=\"http://auth/bub/script.js\"></script></head><body>body5</body></html>"},
+		{filepath.FromSlash("doc7.html"), "<html><body>doc7 content</body></html>"},
+		{filepath.FromSlash("sect/doc8.html"), "\n\n<h1 id=\"title:0ae308ad73e2f37bd09874105281b5d8\">title</h1>\n\n<p>some <em>content</em></p>\n"},
 	}
 
 	for _, test := range tests {
@@ -363,8 +364,8 @@
 func TestAbsUrlify(t *testing.T) {
 	hugofs.DestinationFS = new(afero.MemMapFs)
 	sources := []source.ByteSource{
-		{"sect/doc1.html", []byte("<!doctype html><html><head></head><body><a href=\"#frag1\">link</a></body></html>")},
-		{"content/blue/doc2.html", []byte("---\nf: t\n---\n<!doctype html><html><body>more content</body></html>")},
+		{filepath.FromSlash("sect/doc1.html"), []byte("<!doctype html><html><head></head><body><a href=\"#frag1\">link</a></body></html>")},
+		{filepath.FromSlash("content/blue/doc2.html"), []byte("---\nf: t\n---\n<!doctype html><html><body>more content</body></html>")},
 	}
 	for _, canonify := range []bool{true, false} {
 		viper.Set("CanonifyUrls", canonify)
@@ -399,7 +400,7 @@
 
 		for _, test := range tests {
 
-			file, err := hugofs.DestinationFS.Open(test.file)
+			file, err := hugofs.DestinationFS.Open(filepath.FromSlash(test.file))
 			if err != nil {
 				t.Fatalf("Unable to locate rendered content: %s", test.file)
 			}
@@ -454,10 +455,10 @@
 Front Matter with Ordered Pages 4. This is longer content`)
 
 var WEIGHTED_SOURCES = []source.ByteSource{
-	{"sect/doc1.md", WEIGHTED_PAGE_1},
-	{"sect/doc2.md", WEIGHTED_PAGE_2},
-	{"sect/doc3.md", WEIGHTED_PAGE_3},
-	{"sect/doc4.md", WEIGHTED_PAGE_4},
+	{filepath.FromSlash("sect/doc1.md"), WEIGHTED_PAGE_1},
+	{filepath.FromSlash("sect/doc2.md"), WEIGHTED_PAGE_2},
+	{filepath.FromSlash("sect/doc3.md"), WEIGHTED_PAGE_3},
+	{filepath.FromSlash("sect/doc4.md"), WEIGHTED_PAGE_4},
 }
 
 func TestOrderedPages(t *testing.T) {
@@ -519,10 +520,10 @@
 }
 
 var GROUPED_SOURCES = []source.ByteSource{
-	{"sect1/doc1.md", WEIGHTED_PAGE_1},
-	{"sect1/doc2.md", WEIGHTED_PAGE_2},
-	{"sect2/doc3.md", WEIGHTED_PAGE_3},
-	{"sect3/doc4.md", WEIGHTED_PAGE_4},
+	{filepath.FromSlash("sect1/doc1.md"), WEIGHTED_PAGE_1},
+	{filepath.FromSlash("sect1/doc2.md"), WEIGHTED_PAGE_2},
+	{filepath.FromSlash("sect2/doc3.md"), WEIGHTED_PAGE_3},
+	{filepath.FromSlash("sect3/doc4.md"), WEIGHTED_PAGE_4},
 }
 
 func TestGroupedPages(t *testing.T) {
@@ -711,9 +712,9 @@
 func TestWeightedTaxonomies(t *testing.T) {
 	hugofs.DestinationFS = new(afero.MemMapFs)
 	sources := []source.ByteSource{
-		{"sect/doc1.md", PAGE_WITH_WEIGHTED_TAXONOMIES_1},
-		{"sect/doc2.md", PAGE_WITH_WEIGHTED_TAXONOMIES_2},
-		{"sect/doc3.md", PAGE_WITH_WEIGHTED_TAXONOMIES_3},
+		{filepath.FromSlash("sect/doc1.md"), PAGE_WITH_WEIGHTED_TAXONOMIES_1},
+		{filepath.FromSlash("sect/doc2.md"), PAGE_WITH_WEIGHTED_TAXONOMIES_2},
+		{filepath.FromSlash("sect/doc3.md"), PAGE_WITH_WEIGHTED_TAXONOMIES_3},
 	}
 	taxonomies := make(map[string]string)
 
--- a/hugolib/site_url_test.go
+++ b/hugolib/site_url_test.go
@@ -2,6 +2,7 @@
 
 import (
 	"html/template"
+	"path/filepath"
 	"testing"
 
 	"github.com/spf13/afero"
@@ -47,8 +48,8 @@
 }
 
 var urlFakeSource = []source.ByteSource{
-	{"content/blue/doc1.md", []byte(SLUG_DOC_1)},
-	{"content/blue/doc2.md", []byte(SLUG_DOC_2)},
+	{filepath.FromSlash("content/blue/doc1.md"), []byte(SLUG_DOC_1)},
+	{filepath.FromSlash("content/blue/doc2.md"), []byte(SLUG_DOC_2)},
 }
 
 func TestPageCount(t *testing.T) {
@@ -93,7 +94,7 @@
 		"sd3/index.html",
 		"sd4.html",
 	} {
-		if _, err := hugofs.DestinationFS.Open(s); err != nil {
+		if _, err := hugofs.DestinationFS.Open(filepath.FromSlash(s)); err != nil {
 			t.Errorf("No alias rendered: %s", s)
 		}
 	}
--- a/source/file.go
+++ b/source/file.go
@@ -14,12 +14,10 @@
 package source
 
 import (
+	"github.com/spf13/hugo/helpers"
 	"io"
-	"path"
 	"path/filepath"
 	"strings"
-
-	"github.com/spf13/hugo/helpers"
 )
 
 type File struct {
@@ -65,7 +63,7 @@
 	if f.logicalName != "" {
 		return f.logicalName
 	} else {
-		_, f.logicalName = path.Split(f.relpath)
+		_, f.logicalName = filepath.Split(f.relpath)
 		return f.logicalName
 	}
 }
@@ -78,7 +76,7 @@
 	if f.dir != "" {
 		return f.dir
 	} else {
-		f.dir, _ = path.Split(f.relpath)
+		f.dir, _ = filepath.Split(f.relpath)
 		return f.dir
 	}
 }
--- a/source/filesystem_windows_test.go
+++ b/source/filesystem_windows_test.go
@@ -8,8 +8,8 @@
 var platformBase = "C:\\foo\\"
 var platformPaths = []TestPath{
 	{"foobar", "foobar", "aaa", "", ""},
-	{"b\\1file", "1file", "aaa", "b", "b/"},
-	{"c\\d\\2file", "2file", "aaa", "c", "c/d/"},
-	{"C:\\foo\\e\\f\\3file", "3file", "aaa", "e", "e/f/"}, // note volume case is equal to platformBase
-	{"section\\foo.rss", "foo.rss", "aaa", "section", "section/"},
+	{"b\\1file", "1file", "aaa", "b", "b\\"},
+	{"c\\d\\2file", "2file", "aaa", "c", "c\\d\\"},
+	{"C:\\foo\\e\\f\\3file", "3file", "aaa", "e", "e\\f\\"}, // note volume case is equal to platformBase
+	{"section\\foo.rss", "foo.rss", "aaa", "section", "section\\"},
 }
--- a/target/alias_test.go
+++ b/target/alias_test.go
@@ -1,6 +1,7 @@
 package target
 
 import (
+	"path/filepath"
 	"testing"
 )
 
@@ -13,14 +14,14 @@
 		expected string
 	}{
 		{"", ""},
-		{"s", "s/index.html"},
-		{"/", "/index.html"},
-		{"alias 1", "alias-1/index.html"},
-		{"alias 2/", "alias-2/index.html"},
+		{"s", filepath.FromSlash("s/index.html")},
+		{"/", filepath.FromSlash("/index.html")},
+		{"alias 1", filepath.FromSlash("alias-1/index.html")},
+		{"alias 2/", filepath.FromSlash("alias-2/index.html")},
 		{"alias 3.html", "alias-3.html"},
 		{"alias4.html", "alias4.html"},
-		{"/alias 5.html", "/alias-5.html"},
-		{"/трям.html", "/трям.html"},
+		{"/alias 5.html", filepath.FromSlash("/alias-5.html")},
+		{"/трям.html", filepath.FromSlash("/трям.html")},
 	}
 
 	for _, test := range tests {
--- a/target/page.go
+++ b/target/page.go
@@ -32,7 +32,7 @@
 }
 
 func (pp *PagePub) Translate(src string) (dest string, err error) {
-	if src == "/" {
+	if src == helpers.FilePathSeparator {
 		if pp.PublishDir != "" {
 			return filepath.Join(pp.PublishDir, "index.html"), nil
 		}
--- a/target/page_test.go
+++ b/target/page_test.go
@@ -1,6 +1,7 @@
 package target
 
 import (
+	"path/filepath"
 	"testing"
 )
 
@@ -24,13 +25,14 @@
 
 	for _, test := range tests {
 		f := new(PagePub)
-		dest, err := f.Translate(test.content)
+		dest, err := f.Translate(filepath.FromSlash(test.content))
+		expected := filepath.FromSlash(test.expected)
 		if err != nil {
 			t.Fatalf("Translate returned and unexpected err: %s", err)
 		}
 
-		if dest != test.expected {
-			t.Errorf("Tranlate expected return: %s, got: %s", test.expected, dest)
+		if dest != expected {
+			t.Errorf("Translate expected return: %s, got: %s", expected, dest)
 		}
 	}
 }
@@ -53,7 +55,7 @@
 				t.Fatalf("Translated returned and err: %s", err)
 			}
 
-			if dest != test.expected {
+			if dest != filepath.FromSlash(test.expected) {
 				t.Errorf("Translate expected: %s, got: %s", test.expected, dest)
 			}
 		}
@@ -73,7 +75,7 @@
 
 	for _, test := range tests {
 		f := &PagePub{UglyUrls: true}
-		dest, err := f.Translate(test.content)
+		dest, err := f.Translate(filepath.FromSlash(test.content))
 		if err != nil {
 			t.Fatalf("Translate returned an unexpected err: %s", err)
 		}
@@ -87,7 +89,7 @@
 func TestTranslateDefaultExtension(t *testing.T) {
 	f := &PagePub{DefaultExtension: ".foobar"}
 	dest, _ := f.Translate("baz")
-	if dest != "baz/index.foobar" {
+	if dest != filepath.FromSlash("baz/index.foobar") {
 		t.Errorf("Translate expected return: %s, got %s", "baz/index.foobar", dest)
 	}
 }
--- a/tpl/template.go
+++ b/tpl/template.go
@@ -603,6 +603,7 @@
 	buffer := new(bytes.Buffer)
 	worked := false
 	for _, layout := range layouts {
+
 		name := layout
 
 		if localTemplates.Lookup(name) == nil {
@@ -701,7 +702,7 @@
 
 }
 
-func (t *GoHtmlTemplate) generateTemplateNameFrom(base, path string) string {
+func (t *GoHtmlTemplate) GenerateTemplateNameFrom(base, path string) string {
 	return filepath.ToSlash(path[len(base)+1:])
 }
 
@@ -720,7 +721,7 @@
 				return nil
 			}
 
-			tplName := t.generateTemplateNameFrom(absPath, path)
+			tplName := t.GenerateTemplateNameFrom(absPath, path)
 
 			if prefix != "" {
 				tplName = strings.Trim(prefix, "/") + "/" + tplName