Allow the install of any emscripten-releases build (#732)

This is done by detecting the 40 character git sha in the SDK version
and creating a new set of tools based on this SHA.  This works for any
command that uses expand_sdk_name (i.e. install activate and update) but
it wont show up in the output of `list`.

For example:

./emsdk install sdk-releases-upstream-b0cfdb236483b6828ee2e3f263fd94f011ed1863-64bit

Or just:

./emsdk install releases-upstream-b0cfdb236483b6828ee2e3f263fd94f011ed1863
diff --git a/emsdk.py b/emsdk.py
index f1f8c12..4d44cb4 100644
--- a/emsdk.py
+++ b/emsdk.py
@@ -49,6 +49,8 @@
 
 zips_subdir = 'zips/'
 
+extra_release_tag = None
+
 # Enable this to do very verbose printing about the different steps that are
 # being run. Useful for debugging.
 VERBOSE = int(os.getenv('EMSDK_VERBOSE', '0'))
@@ -2202,12 +2204,16 @@
   tags = []
   tags_fastcomp = []
   info = load_releases_info()
+
   for version, sha in sorted(info['releases'].items(), key=lambda x: version_key(x[0])):
     tags.append(sha)
     # Only include versions older than 1.39.0 in fastcomp releases
     if version_key(version) < (2, 0, 0):
       tags_fastcomp.append(sha)
 
+  if extra_release_tag:
+    tags.append(extra_release_tag)
+
   # Add the tip-of-tree, if it exists.
   if os.path.exists(tot_path()):
     tot = find_tot()
@@ -2248,6 +2254,7 @@
     for tool_name in sdk.uses:
       tool = find_tool(tool_name)
       if not tool:
+        debug_print('missing dependency: ' + tool_name)
         return False
     return True
 
@@ -2641,6 +2648,7 @@
     releases_info = load_releases_info()['releases']
     release_hash = get_release_hash(version, releases_info)
     if release_hash:
+      # Known release hash
       if backend == 'fastcomp' and version_key(version) >= (2, 0, 0):
         exit_with_fastcomp_error()
       if backend is None:
@@ -2649,6 +2657,10 @@
         else:
           backend = 'fastcomp'
       return 'sdk-releases-%s-%s-64bit' % (backend, release_hash)
+    elif len(version) == 40:
+      global extra_release_tag
+      extra_release_tag = version
+      return 'sdk-releases-%s-%s-64bit' % (backend, version)
   return name
 
 
@@ -2803,6 +2815,10 @@
     global TTY_OUTPUT
     TTY_OUTPUT = False
 
+  # Replace meta-packages with the real package names.
+  if cmd in ('update', 'install', 'activate'):
+    args = [expand_sdk_name(a) for a in args]
+
   load_dot_emscripten()
   load_sdk_manifest()
 
@@ -2835,10 +2851,6 @@
         return 1
   args = [x for x in args if x]
 
-  # Replace meta-packages with the real package names.
-  if cmd in ('update', 'install', 'activate'):
-    args = [expand_sdk_name(a) for a in args]
-
   if cmd == 'list':
     print('')
 
diff --git a/scripts/test.py b/scripts/test.py
index f924879..e8dd7f4 100755
--- a/scripts/test.py
+++ b/scripts/test.py
@@ -136,8 +136,8 @@
 ''')
 
   def setUp(self):
-    checked_call_with_output(emsdk + ' install latest')
-    checked_call_with_output(emsdk + ' activate latest')
+    run_emsdk('install latest')
+    run_emsdk('activate latest')
 
   def test_already_installed(self):
     # Test we don't re-download unnecessarily
@@ -253,6 +253,13 @@
     print('second time')
     run_emsdk('update')
 
+  def test_install_arbitrary(self):
+    # Test that its possible to install arbrary emscripten-releases SDKs
+    run_emsdk('install sdk-releases-upstream-5c776e6a91c0cb8edafca16a652ee1ee48f4f6d2')
+
+    # Check that its not re-downloaded
+    checked_call_with_output(emsdk + ' install sdk-releases-upstream-5c776e6a91c0cb8edafca16a652ee1ee48f4f6d2', expected='Skipped', unexpected='Downloading:')
+
 
 if __name__ == '__main__':
   unittest.main(verbosity=2)