summaryrefslogtreecommitdiffstats
path: root/nikola/winutils.py
diff options
context:
space:
mode:
Diffstat (limited to 'nikola/winutils.py')
-rw-r--r--nikola/winutils.py120
1 files changed, 69 insertions, 51 deletions
diff --git a/nikola/winutils.py b/nikola/winutils.py
index 517a326..712de39 100644
--- a/nikola/winutils.py
+++ b/nikola/winutils.py
@@ -26,74 +26,92 @@
"""windows utilities to workaround problems with symlinks in a git clone"""
+from __future__ import print_function, unicode_literals
import os
import shutil
-import sys
-# don't add imports outside stdlib, will be imported in setup.py
+# don't add imports to nikola code, will be imported in setup.py
-def should_fix_git_symlinked():
- """True if git symlinls markers should be filled with the real content"""
- if sys.platform == 'win32':
- path = (os.path.dirname(__file__) +
- r'\data\samplesite\stories\theming.rst')
- try:
- if os.path.getsize(path) < 200:
- return True
- except Exception:
- pass
- return False
+def is_file_into_dir(filename, dirname):
+ try:
+ res = not os.path.relpath(filename, dirname).startswith('.')
+ except ValueError:
+ res = False
+ return res
-def fix_git_symlinked(src, dst):
- """fix git symlinked files in windows that had been copied from src to dst
+def fix_all_git_symlinked(topdir):
+ """inplace conversion of git symlinks to real content
Most (all?) of git implementations in windows store a symlink pointing
into the repo as a text file, the text being the relative path to the
file with the real content.
So, in a clone of nikola in windows the symlinked files will have the
- wrong content.
+ wrong content; a .zip download from Github has the same problem.
- The linux usage pattern for those files is 'copy to some dir, then use',
- so we inspect after the copy and rewrite the wrong contents.
+ This function will rewrite each symlinked file with the correct contents, but
+ keep in mind that the working copy will be seen as dirty by git after operation.
- The goals are:
- support running nikola from a clone without installing and without
- making dirty the WC.
+ Expects to find a list of symlinked files at nikola/data/symlinked.txt
- support install from the WC.
+ The list can be generated by scripts/generate_symlinked_list.sh , which is
+ basically a redirect of
+ cd nikola_checkout
+ git ls-files -s | awk '/120000/{print $4}'
- if possible and needed, support running the test suite without making
- dirty the WC.
+ Weakness: if interrupted of fail amidst a directory copy, next run will not
+ see the missing files.
"""
- # if running from WC there should be a 'doc' dir sibling to nikola package
- if not should_fix_git_symlinked():
- return
- # probabbly in a WC, so symlinks should be fixed
- for root, dirs, files in os.walk(dst):
- for name in files:
- filename = os.path.join(root, name)
-
- # detect if symlinked
- try:
- if not (2 < os.path.getsize(filename) < 500):
- continue
- # which encoding uses a git symlink marker ? betting on default
- with open(filename, 'r') as f:
- text = f.read()
- if text[0] != '.':
- # de facto hint to skip binary files and exclude.meta
- continue
- except Exception:
- # probably encoding: content binary or encoding not defalt,
- # also in py2.6 it can be path encoding
+ with open(topdir + r'\nikola\data\symlinked.txt', 'rb') as f:
+ all_bytes = f.read()
+ text = all_bytes.decode('utf8')
+ # expect each line a relpath from git or zip root,
+ # smoke test relpaths are relative to git root
+ if text.startswith('.'):
+ raise Exception(r'Bad data in \nikola\data\symlinked.txt')
+ relnames = text.split('\n')
+ relnames = [name.strip().replace('/', '\\') for name in relnames]
+ relnames = [name for name in relnames if name]
+
+ failures = 0
+ for name in relnames:
+ # build dst path and do some basic validation
+ dst = os.path.join(topdir, name)
+ # don't access files outside topdir
+ if not is_file_into_dir(dst, topdir):
+ continue
+ if os.path.isdir(dst):
+ # assume the file was de-symlinked
+ continue
+
+ # build src path and do some basic validation
+ with open(os.path.join(topdir, dst), 'r') as f:
+ text = f.read()
+ dst_dir = os.path.dirname(dst)
+ try:
+ src = os.path.normpath(os.path.join(dst_dir, text))
+ if not os.path.exists(src):
+ # assume the file was de-symlinked before
continue
- dst_dir_relpath = os.path.dirname(os.path.relpath(filename, dst))
- path = os.path.normpath(os.path.join(src, dst_dir_relpath, text))
- if not os.path.exists(path):
+ # don't access files outside topdir
+ if not is_file_into_dir(src, topdir):
continue
- # most probably it is a git symlinked file
+ except Exception:
+ # assume the file was de-symlinked before
+ continue
+
+ # copy src to dst
+ try:
+ if os.path.isdir(src):
+ os.unlink(dst)
+ shutil.copytree(src, dst)
+ else:
+ shutil.copy2(src, dst)
+ except Exception:
+ failures += 1
+ print("*** copy failed for")
+ print("\t src:", src)
+ print("\t dst:", dst)
- # copy original content to filename
- shutil.copy(path, filename)
+ return failures